这交互炸了:饿了么是怎么让Image变成详情页的

晚上叫外卖,打开饿了么,发现推了一个版本,更新之后,点开了个鸡腿,哇,交互炫炸了。php

本文同步自wing的地方酒馆java

不过仍是有槽点。我是无心中才发现能够左右滑动的。这。。。你不告诉我,我怎么知道左右能够滑。android

github.com/githubwing/…git

直接上图啊:github

挺有意思的,对吧? 因此我就想模仿一下。下面是我作出来的效果:ide

额。。不过图片不是长条的哈。大概意思同样就好了。接下来将和你们分享这个效果是如何实现的。讲思路以及遇到的问题。可是不会讨论细节,具体的细节请看源码。布局

他是一个Activity仍是两个?

相信你确定有这样的疑问,答案是一个。你看到的中间imageview是viewpager。在Viewpager上面是一个透明的View。固然,这个Activity的背景也是透明的。post

实现思路

我使用CoordinatorLayout+Behavior实现的。说实话,Behavior真心强大。。性能

viewpager+头部

整个实现的思路是这样的。总体布局从上到下依次是:动画

  • 透明View
  • viewpager
  • RecyclerView

其中透明View和Viewpager 合并成一个自定义的Header。当这个Header上移的时候,图片放大,而且RecyclerView联动上衣,从透明转向而且不透明。

因此首先要定制一个透明的可移动的HeaderView。

在onTouchEvent处理一下手势。。

@Override public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        return true;
      case MotionEvent.ACTION_MOVE:
         if(上下移动到阀值){
             展开为详情()
         }else if(上下滑动到阀值,恢复viewpager){
         }else if(下滑,则关闭Activity)复制代码

将header分为三种状态:

  • 上移。则展开为详情页。
  • 下移,则恢复为viewpager。
  • 再下移,则finish Activity。

在上移的过程当中,遇到了一点小挑战,这里分享下:

上移的过程当中,图片须要放大。可是在作的过程当中,不能使用LayoutParams实现。这里就关系到一些动画的小细节。

动画使用LayoutParams实现是一个禁忌。他会致使不停requestLayout,从而影响UI性能。

因此这里个人一个解法就是,我放大图片,不是真正的改变ImageView大小,而是去Scale图片。即便看起来变大了,他的View真正大小也不会变。

因此,有一句话叫作真亦是假、假亦是真 真真假假,你又何须当真呢?动画效果只要遵循这句话,基本上都是能够实现的。你所看到的效果都是假的。都是障眼法。View变大不是真正的变大。View悬浮不是真正的悬浮(有多是显隐)。就像变魔术同样。。其实很简单。

接下来又遇到问题了。图片放大了,文字如何对齐? 文字的位置固然也不能真正改变。因此这里使用TranslationX实现。在图片放大的过程当中,使用scale的系数,与两个端点值进行一个线性变化计算。主要文字对齐代码以下:

bottom.offsetLeftAndRight(
            (int) (target.getWidth() / 2 - target.getWidth() * (1 + progress) / 2
                + MarginConfig.MARGIN_LEFT_RIGHT - bottom.getX()));复制代码

第二个点。就是在图片放大过程当中,底部文字和按钮左右padding不能变。这也是我没有封装成一个拿来就用的View的缘由(其实仍是水平不够)。由于这些空间须要所有按照上方的方法进行动态计算。。因此也是比较坑爹的。。

ViewPager

拿了网上一个画廊的效果。直接

setPageTransformer(true, new ZoomOutPageTransformer());复制代码

这里注意,须要改变一下view的绘制顺序,保证当前view是最后绘制处于最上层

/改变系统绘制顺序
  @Override protected int getChildDrawingOrder(int childCount, int i) {

    int position = getCurrentItem();
    if(position<0){
      return i;
    }else{
      if(i == childCount - 1){//这是最后一个须要刷新的item
        if(position>i){
          position=i;
        }
        return position;
      }
      if(i == position){//这是本来要在最后一个刷新的item
        return childCount - 1;
      }
    }
    return i;
  }
}复制代码

RecyclerView

RecyclerView最开始是彻底透明的。而且跟随HeaderView上移而上移,在上移的过程当中渐渐显示出来。 须要监听RecyclerView滚动,当RecyclerView滚动到顶部的时候。告知Header,该恢复最初原样了。

@Override
  public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {

    //向下Fling而且到顶部
    if (velocityY < 0 && ((RecyclerView) target).getChildAt(0).getY() == 0) {
      mDependency.restore(mDependency.getY());
    }
    return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
  }

  @Override
  public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {

    //若是在顶部
    if (((RecyclerView) target).getChildAt(0).getY() == 0) {
      //向下滑动
      if (dy < 0) {
        mDependency.setY(mDependency.getY() - dy);
        //小于阀值
        if (mDependency.getY() < 500) {
          mDependency.restore(mDependency.getY());
        }
      }
    }
    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
  }
}复制代码

Behavior

让header和RecyclerView关联起来的就是Behavior了。Behavior以前写过几篇介绍过了,这里就再也不啰嗦。

denpendcy为HeaderView。而且监听RecyclerView的滑动。

具体的细节仍是看源码吧~

若是你以为还不错,欢迎Star
本项目地址

欢迎加入个人qq群: 425983695

第一期:这交互炸了:饿了么是怎么让Image变成详情页的

第二期:这交互炸了 (二):爱范儿是如何让详情页缩小为横向列表的

第三期:这交互炸了(三) :不看后悔!你必定没见过这样的闪屏