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

本文已受權微信公衆號:鴻洋(hongyangAndroid)原創首發java

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

不過仍是有槽點。我是無心中才發現能夠左右滑動的。這。。。你不告訴我,我怎麼知道左右能夠滑。github

https://github.com/githubwing/ZoomHeader微信

直接上圖啊:markdown

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

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

他是一個Activity仍是兩個?

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

實現思路

我使用CoordinatorLayout+Behavior實現的。說實話,Behavior真心強大。。動畫

viewpager+頭部

整個實現的思路是這樣的。總體佈局從上到下依次是:spa

  • 透明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

相關文章
相關標籤/搜索