自定义Behavior —— 仿知乎,FloatActionButton隐藏与展示

rsxd4147 8年前
   <p>前段时间写了一篇博客 使用CoordinatorLayout打造各种炫酷的效果 ,主要介绍了APPBarLayout和CollapsingToolbarLayout的基本用法,AppBarLayout主要用来实现在滚动的时候ToolBar的 隐藏于展示,CollapsingToolbarLayout主要用来实现parallax和pin等效果。如果你对CoordinatorLayout还不了解的话,请先阅读这篇文章。</p>    <p>写作思路</p>    <ul>     <li>CoordinatorLayout Behavior 简介</li>     <li>怎样自定义 Behavior</li>     <li>仿知乎效果 自定义 Behavior 的实现</li>     <li>自定义 Behavior 两种方法的 对比</li>     <li>FloatActionButton 自定义 Behavior 效果的实现</li>     <li>题外话</li>    </ul>    <p>今天就来讲解怎样通过自定义behavior来实现各种炫酷的效果 ,效果图如下</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/935fde58fab29692d5dc918194b73fd7.gif"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/eaa0550f271175b5a6a581cccca47d99.gif"></p>    <h2>下面让我们一起来看一下怎样实现仿知乎的效果</h2>    <p>老规矩,先看代码</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <android.support.design.widget.CoordinatorLayout      android:id="@+id/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/index_app_bar"          theme="@style/AppTheme.AppBarOverlay"          android:layout_width="match_parent"          android:layout_height="wrap_content">              <RelativeLayout              android:layout_width="match_parent"              android:layout_height="?attr/actionBarSize"              android:background="@color/colorPrimary"              app:layout_scrollFlags="scroll|enterAlways">                  <ImageView                  android:id="@+id/search"                  android:layout_width="24dp"                  android:layout_height="24dp"                  android:layout_centerVertical="true"                  android:layout_marginLeft="10dp"                  android:src="@drawable/search"/>                <TextView                  android:layout_width="match_parent"                  android:layout_height="wrap_content"                  android:layout_centerVertical="true"                  android:layout_marginLeft="10dp"                  android:layout_toRightOf="@id/search"                  android:text="搜索话题、问题或人"                  android:textSize="16sp"/>            </RelativeLayout>          </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">        </android.support.v7.widget.RecyclerView>          <!--使用RadioGroup来实现tab的切换-->      <RadioGroup          android:id="@+id/rg"          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:layout_gravity="bottom"          android:background="@color/bg_tab"          android:orientation="horizontal"          app:layout_behavior="@string/behavior_footer"          >            <RadioButton              android:id="@+id/rb_home"              style="@style/bottom_tab"              android:drawableTop="@drawable/sel_home"              android:text="Home"/>            <RadioButton              android:id="@+id/rb_course"              style="@style/bottom_tab"              android:drawableTop="@drawable/sel_course"              android:text="course"/>            <RadioButton              android:id="@+id/rb_direct_seeding"              style="@style/bottom_tab"              android:drawableTop="@drawable/sel_direct_seeding"              android:text="direct"/>            <RadioButton              android:id="@+id/rb_me"              style="@style/bottom_tab"              android:drawableTop="@drawable/sel_me"              android:text="me"/>        </RadioGroup>      </android.support.design.widget.CoordinatorLayout></code></pre>    <pre>  <code class="language-java"><style name="bottom_tab">      <item name="android:layout_width">0dp</item>      <item name="android:layout_height">60dp</item>      <item name="android:layout_weight">1</item>      <item name="android:text">0dp</item>      <item name="android:gravity">center</item>      <item name="android:textColor">@drawable/sel_bottom_tab_text</item>      <item name="android:padding">10dp</item>      <item name="android:button">@null</item>  </style>  <style name="bottom_tab">      <item name="android:layout_width">0dp</item>      <item name="android:layout_height">60dp</item>      <item name="android:layout_weight">1</item>      <item name="android:text">0dp</item>      <item name="android:gravity">center</item>      <item name="android:textColor">@drawable/sel_bottom_tab_text</item>      <item name="android:padding">10dp</item>      <item name="android:button">@null</item>  </style></code></pre>    <h3>思路分析</h3>    <p>根据动态如可以看到,主要有两个效果</p>    <ul>     <li>上面的AppBarLayout 向上滑动的时候会隐藏,向下滑动的时候会展示,说白了就是给APPLayout的子View Relativelayout 设置 app:layout_scrollFlags="scroll|enterAlways",核心代码如下</li>    </ul>    <pre>  <code class="language-java"><android.support.design.widget.AppBarLayout      android:id="@+id/index_app_bar"      theme="@style/AppTheme.AppBarOverlay"      android:layout_width="match_parent"      android:layout_height="wrap_content">          <RelativeLayout          android:layout_width="match_parent"          android:layout_height="?attr/actionBarSize"          android:background="@color/colorPrimary"          app:layout_scrollFlags="scroll|enterAlways">            ----        </RelativeLayout>   </android.support.design.widget.AppBarLayout></code></pre>    <ul>     <li>下面的 RadioGroup ,我们可以看到,向上 滑动的时候会隐藏,向下滑动的时候会显示,其实我们只是给其设置了 behavior 而已 app:layout_behavior="@string/behavior_footer",那这个behavior_footer是什么东西,别急 ,下面就是介绍了</li>    </ul>    <pre>  <code class="language-java"><string name="behavior_footer">com.xujun.contralayout.behavior.FooterBehavior</string></code></pre>    <h2>Behavior简介</h2>    <p><img src="https://simg.open-open.com/show/8ed038aa9f8977c642649638b6260a9c.png"></p>    <p>Behavior是CoordinatorLayout里面的一个内部类,通过它我们可以与 CoordinatorLayout的一个或者多个子View进行交互,包括 drag,swipes, flings等手势动作。</p>    <p>今天 我们主要着重介绍里面的几个方法</p>    <table>     <thead>      <tr>       <th>方法</th>       <th>解释</th>      </tr>     </thead>     <tbody>      <tr>       <td>boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency)</td>       <td>确定child View 是否有一个特定的兄弟View作为布局的依赖(即dependency)</td>      </tr>      <tr>       <td>boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)</td>       <td>当child View 的 dependent view 发生变化的时候,这个方法会调用</td>      </tr>      <tr>       <td>boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes)</td>       <td>当CoordinatorLayout 的直接或者非直接子View开始准备嵌套滑动的时候会调用</td>      </tr>      <tr>       <td>void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)</td>       <td>当嵌套滑动的 时候,target尝试滑动或者正在滑动的 时候会调用</td>      </tr>     </tbody>    </table>    <p>关于更多方法,请参考官 <a href="/misc/goto?guid=4959639396394594440" rel="nofollow,noindex">网文档说明</a></p>    <h2>怎样自定义Behavior</h2>    <p>前面已经说到,今天主要介绍四个方法,这里我们把它分为两组。</p>    <p>第一组</p>    <pre>  <code class="language-java">// 决定child 依赖于把一个 dependency  boolean    layoutDependsOn(CoordinatorLayout parent, V child, View dependency)    // 当 dependency View 改变的时候 child 要做出怎样的响应  boolean    onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)</code></pre>    <p>第二组</p>    <pre>  <code class="language-java">// 当CoordinatorLayout的直接或者非直接子View开始嵌套滑动的时候,会调用这个方法  boolean    onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes)    // 当嵌套滑动的时候,target 尝试滑动或者正在滑动会调用这个方法  onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)</code></pre>    <h3>首先我们先看第一组是怎样实现的?</h3>    <pre>  <code class="language-java">/**   * 知乎效果底部behavior 依赖于 AppBarLayout   *   * @author xujun  on 2016/11/30.   * @email gdutxiaoxu@163.com   */    public class FooterBehaviorDependAppBar extends CoordinatorLayout.Behavior<View> {        public static final String TAG = "xujun";        public FooterBehaviorDependAppBar(Context context, AttributeSet attrs) {          super(context, attrs);      }        //当 dependency instanceof AppBarLayout 返回TRUE,将会调用onDependentViewChanged()方法      @Override      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {          return   dependency instanceof AppBarLayout;      }        @Override      public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {          //根据dependency top值的变化改变 child 的 translationY          float translationY = Math.abs(dependency.getTop());          child.setTranslationY(translationY);          Log.i(TAG, "onDependentViewChanged: " + translationY);          return true;        }  }</code></pre>    <p>思路分析</p>    <p>这里我们要分清两个概念,child 和 dependency ,child 是我们要改变的坐标的view,而 dependency 是child 的 附属 ,即child 会随着 dependency 坐标的改变而改变。</p>    <p>比如上面的例子:当我们把 app:layout_behavior="com.xujun.contralayout.behavior.FooterBehaviorDependAppBar" 设置给 RadioGroup 的时候,这时候 child 就是 RadioGroup ,而 dependency 就是 APPBarLayout ,因为我们在 layoutDependsOn 方法里面 ,返回 dependency instanceof AppBarLayout ,即当 dependency 是 AppBarLayout 或者 AppBarLayout的子类的时候返回TRUE。</p>    <pre>  <code class="language-java">//当 dependency instanceof AppBarLayout 返回TRUE,将会调用onDependentViewChanged()方法      @Override      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {          return   dependency instanceof AppBarLayout;      }</code></pre>    <p>而之所以 RadioGroup 在向上滑动的时候会隐藏,向下滑动的时候会显示,是因为我们在 onDependentViewChanged 方法的时候 动态地根据 dependency 的 top 值改变 RadioGroup 的 translationY 值,核心 代码如下</p>    <pre>  <code class="language-java">@Override      public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {          //根据dependency top值的变化改变 child 的 translationY          float translationY = Math.abs(dependency.getTop());          child.setTranslationY(translationY);          Log.i(TAG, "onDependentViewChanged: " + translationY);          return true;        }</code></pre>    <p>到此第一种思路分析为止</p>    <h3>第二种思路</h3>    <p>主要是根据 onStartNestedScroll() 和 onNestedPreScroll()方法 来实现的,</p>    <ul>     <li>当我们开始滑动的时候,我们判断是否是垂直滑动,如果是返回TRUE,否则返回 FALSE,返回TRUE,会接着调用onNestedPreScroll()等一系列方法。</li>    </ul>    <pre>  <code class="language-java">//1.判断滑动的方向 我们需要垂直滑动      @Override      public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,                                         View directTargetChild, View target, int nestedScrollAxes) {          return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;      }</code></pre>    <ul>     <li>在 onNestedPreScroll() 方法里面,我们根据我们的逻辑来决定是否显示 target , 在这里我们是向上上滑动的时候,如果我们滑动的距离超过 target 的高度 并且 当前是可见的状态下,我们执行动画,隐藏 target,当我们向下滑动的时候,并且 View 是不可见的情况下,我们执行动画 ,显示target</li>    </ul>    <pre>  <code class="language-java">//2.根据滑动的距离显示和隐藏footer view      @Override      public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child,                                    View target, int dx, int dy, int[] consumed) {          if (dy > 0 && sinceDirectionChange < 0 || dy < 0 && sinceDirectionChange > 0) {              child.animate().cancel();              sinceDirectionChange = 0;          }          sinceDirectionChange += dy;          int visibility = child.getVisibility();          if (sinceDirectionChange > child.getHeight() && visibility == View.VISIBLE) {              hide(child);          } else {              if (sinceDirectionChange < 0 && (visibility == View.GONE || visibility == View                      .INVISIBLE)) {                  show(child);              }          }      }</code></pre>    <p>全部代码如下</p>    <pre>  <code class="language-java">/**   * 知乎效果底部 behavior   *   * @author xujun  on 2016/11/30.   * @email gdutxiaoxu@163.com   */    public class FooterBehavior extends CoordinatorLayout.Behavior<View> {        private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();        private int sinceDirectionChange;        public FooterBehavior(Context context, AttributeSet attrs) {          super(context, attrs);      }        //1.判断滑动的方向 我们需要垂直滑动      @Override      public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,                                         View directTargetChild, View target, int nestedScrollAxes) {          return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;      }        //2.根据滑动的距离显示和隐藏footer view      @Override      public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child,                                    View target, int dx, int dy, int[] consumed) {          if (dy > 0 && sinceDirectionChange < 0 || dy < 0 && sinceDirectionChange > 0) {              child.animate().cancel();              sinceDirectionChange = 0;          }          sinceDirectionChange += dy;          int visibility = child.getVisibility();          if (sinceDirectionChange > child.getHeight() && visibility == View.VISIBLE) {              hide(child);          } else {              if (sinceDirectionChange < 0 && (visibility == View.GONE || visibility == View                      .INVISIBLE)) {                  show(child);              }          }      }        private void hide(final View view) {          ViewPropertyAnimator animator = view.animate().translationY(view.getHeight()).                  setInterpolator(INTERPOLATOR).setDuration(200);          animator.setListener(new Animator.AnimatorListener() {              @Override              public void onAnimationStart(Animator animator) {                }                @Override              public void onAnimationEnd(Animator animator) {                  view.setVisibility(View.GONE);              }                @Override              public void onAnimationCancel(Animator animator) {                  show(view);              }                @Override              public void onAnimationRepeat(Animator animator) {                }          });          animator.start();      }        private void show(final View view) {          ViewPropertyAnimator animator = view.animate().translationY(0).                  setInterpolator(INTERPOLATOR).                  setDuration(200);          animator.setListener(new Animator.AnimatorListener() {              @Override              public void onAnimationStart(Animator animator) {                }                @Override              public void onAnimationEnd(Animator animator) {                  view.setVisibility(View.VISIBLE);              }                @Override              public void onAnimationCancel(Animator animator) {                  hide(view);              }                @Override              public void onAnimationRepeat(Animator animator) {                }          });          animator.start();        }  }</code></pre>    <h2>两种实现方法的对比和总结</h2>    <ul>     <li> <p>我们知道第一种方法我们主要是重写layoutDependsOn 和 onDependentViewChanged 这两个方法,这个方法在 layoutDependsOn 判断 dependency 是否是 APpBarLayout 的实现类,所以 会导致 child 依赖于 AppBarLayout,灵活性不是太强</p> </li>     <li> <p>而第二种方法,我们主要是重写 onStartNestedScroll 和 onNestedPreScroll 这两个方法,判断是否是垂直滑动,是的话就进行处理,灵活性大大增强,推荐使用这一种方法</p> </li>     <li> <p>需要注意的是不管是第一种方法,还是第二种方法,我们都需要重写带两个构造方法的函数,因为底层机制会采用反射的形式获得该对象</p> </li>    </ul>    <pre>  <code class="language-java">public FooterBehavior(Context context, AttributeSet attrs) {          super(context, attrs);  }</code></pre>    <h2>自定义 Behavior 实现 FloatingActionButton 的显示与隐藏</h2>    <p>效果图如下</p>    <p>缩放隐藏的</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ea966e6c110a8fa2503571fc361fd6fc.gif"></p>    <p>向上向下隐藏的</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9e5f084c41f3538679a14af37c415eaa.gif"></p>    <h3>布局代码</h3>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <android.support.design.widget.CoordinatorLayout      android:id="@+id/activity_floating_action_button"      xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools"      android:layout_width="match_parent"      android:layout_height="match_parent"      xmlns:app="http://schemas.android.com/apk/res-auto"      tools:context="com.xujun.contralayout.UI.FloatingActionButtonActivity">          <android.support.design.widget.AppBarLayout          android:id="@+id/index_app_bar"          theme="@style/AppTheme.AppBarOverlay"          android:layout_width="match_parent"          android:layout_height="wrap_content">              <RelativeLayout              android:layout_width="match_parent"              android:layout_height="?attr/actionBarSize"              android:background="@color/colorPrimary"              app:layout_scrollFlags="scroll|enterAlways">                  <ImageView                  android:id="@+id/search"                  android:layout_width="24dp"                  android:layout_height="24dp"                  android:layout_centerVertical="true"                  android:layout_marginLeft="10dp"                  android:src="@drawable/search"/>                <TextView                  android:layout_width="match_parent"                  android:layout_height="wrap_content"                  android:layout_centerVertical="true"                  android:layout_marginLeft="10dp"                  android:layout_toRightOf="@id/search"                  android:text="搜索话题、问题或人"                  android:textSize="16sp"/>            </RelativeLayout>          </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">        </android.support.v7.widget.RecyclerView>        <android.support.design.widget.FloatingActionButton          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_gravity="bottom|right|end"          android:layout_marginBottom="40dp"          android:layout_marginRight="25dp"          android:background="@android:color/holo_green_light"          android:src="@drawable/add"          app:layout_behavior="@string/behavior_my_fab_scale"/>    </android.support.design.widget.CoordinatorLayout></code></pre>    <p>如果想使用不同的效果,只需要给 FloatingActionButton 制定不同的 bevaior 即可</p>    <pre>  <code class="language-java">app:layout_behavior="com.xujun.contralayout.behavior.MyFabBehavior"</code></pre>    <h3>自定义behavior 代码</h3>    <pre>  <code class="language-java">/**   *  FloatingActionButton behavior 向上向下隐藏的   * @author xujun  on 2016/12/1.   * @email gdutxiaoxu@163.com   */    public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {        private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();        private float viewY;//控件距离coordinatorLayout底部距离      private boolean isAnimate;//动画是否在进行        public MyFabBehavior(Context context, AttributeSet attrs) {          super(context, attrs);      }        //在嵌套滑动开始前回调      @Override      public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {            if(child.getVisibility() == View.VISIBLE&&viewY==0){              //获取控件距离父布局(coordinatorLayout)底部距离              viewY=coordinatorLayout.getHeight()-child.getY();          }            return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//判断是否竖直滚动      }        //在嵌套滑动进行时,对象消费滚动距离前回调      @Override      public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {          //dy大于0是向上滚动 小于0是向下滚动            if (dy >=0&&!isAnimate&&child.getVisibility()==View.VISIBLE) {              hide(child);          } else if (dy <0&&!isAnimate&&child.getVisibility()==View.GONE) {              show(child);          }      }        //隐藏时的动画      private void hide(final View view) {          ViewPropertyAnimator animator = view.animate().translationY(viewY).setInterpolator(INTERPOLATOR).setDuration(200);            animator.setListener(new Animator.AnimatorListener() {              @Override              public void onAnimationStart(Animator animator) {                  isAnimate=true;              }                @Override              public void onAnimationEnd(Animator animator) {                  view.setVisibility(View.GONE);                  isAnimate=false;              }                @Override              public void onAnimationCancel(Animator animator) {                  show(view);              }                @Override              public void onAnimationRepeat(Animator animator) {              }          });          animator.start();      }        //显示时的动画      private void show(final View view) {          ViewPropertyAnimator animator = view.animate().translationY(0).setInterpolator(INTERPOLATOR).setDuration(200);          animator.setListener(new Animator.AnimatorListener() {              @Override              public void onAnimationStart(Animator animator) {                  view.setVisibility(View.VISIBLE);                  isAnimate=true;              }                @Override              public void onAnimationEnd(Animator animator) {                  isAnimate=false;              }                @Override              public void onAnimationCancel(Animator animator) {                  hide(view);              }                @Override              public void onAnimationRepeat(Animator animator) {              }          });          animator.start();      }  }</code></pre>    <pre>  <code class="language-java">/**   * <p>下拉时显示FAB,上拉隐藏,留出更多位置给用户。</p>   * Created on 2016/12/1.   *   * @author xujun   */  public class ScaleDownShowBehavior extends FloatingActionButton.Behavior {      /**       * 退出动画是否正在执行。       */      private boolean isAnimatingOut = false;        private OnStateChangedListener mOnStateChangedListener;        public ScaleDownShowBehavior(Context context, AttributeSet attrs) {          super();      }        @Override      public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {          return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;      }        @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);              }          }      }        public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {          this.mOnStateChangedListener = mOnStateChangedListener;      }        // 外部监听显示和隐藏。      public interface OnStateChangedListener {          void onChanged(boolean isShow);      }        public static <V extends View> ScaleDownShowBehavior from(V view) {          ViewGroup.LayoutParams params = view.getLayoutParams();          if (!(params instanceof CoordinatorLayout.LayoutParams)) {              throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");          }          CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();          if (!(behavior instanceof ScaleDownShowBehavior)) {              throw new IllegalArgumentException("The view is not associated with ScaleDownShowBehavior");          }          return (ScaleDownShowBehavior) behavior;      }        private ViewPropertyAnimatorListener viewPropertyAnimatorListener = 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;          }      };  }</code></pre>    <p>思路这里就不详细展开了,因为前面在讲解 仿知乎效果的时候已经讲过了,大概就是根据不同的滑动行为执行不同的动画 而已</p>    <h2>题外话</h2>    <ul>     <li>通过这篇文章,熟悉 CoordinatorLayout 的 各种用法,同时也初步理解了自定义Behavior的思路</li>     <li>同时复习了动画的相关知识</li>    </ul>    <p> </p>    <p>来自:http://www.jianshu.com/p/c174edcce58d</p>    <p> </p>