這交互炸了:餓了么是怎么讓Image變成詳情頁的

VirgilioHar 8年前發布 | 8K 次閱讀 Android開發 移動開發 RecyclerView

晚上叫外賣,打開餓了么,發現推了一個版本,更新以后,點開了個雞腿,哇,交互炫炸了。

不過還是有槽點。我是無意中才發現可以左右滑動的。這。。。你不告訴我,我怎么知道左右可以滑。

直接上圖啊:

挺有意思的,對吧? 所以我就想模仿一下。下面是我做出來的效果:

額。。不過圖片不是長條的哈。大概意思一樣就行了。接下來將和大家分享這個效果是如何實現的。講思路以及遇到的問題。但是不會討論細節,具體的細節請看源碼。

他是一個Activity還是兩個?

相信你肯定有這樣的疑問,答案是一個。你看到的中間imageview是viewpager。在Viewpager上面是一個透明的View。當然,這個Activity的背景也是透明的。

實現思路

我使用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的滑動。

 

 

 

 本文由用戶 VirgilioHar 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!