本文已受權微信公衆號:鴻洋(hongyangAndroid)原創首發java
晚上叫外賣,打開餓了麼,發現推了一個版本,更新之後,點開了個雞腿,哇,交互炫炸了。git
不過仍是有槽點。我是無心中才發現能夠左右滑動的。這。。。你不告訴我,我怎麼知道左右能夠滑。github
https://github.com/githubwing/ZoomHeader微信
直接上圖啊:markdown
挺有意思的,對吧? 因此我就想模仿一下。下面是我作出來的效果:ide
額。。不過圖片不是長條的哈。大概意思同樣就好了。接下來將和你們分享這個效果是如何實現的。講思路以及遇到的問題。可是不會討論細節,具體的細節請看源碼。佈局
相信你確定有這樣的疑問,答案是一個。你看到的中間imageview是viewpager。在Viewpager上面是一個透明的View。固然,這個Activity的背景也是透明的。性能
我使用CoordinatorLayout+Behavior實現的。說實話,Behavior真心強大。。動畫
整個實現的思路是這樣的。總體佈局從上到下依次是:spa
其中透明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分爲三種狀態:
在上移的過程當中,遇到了一點小挑戰,這裏分享下:
上移的過程當中,圖片須要放大。可是在作的過程當中,不能使用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的緣由(其實仍是水平不夠)。由於這些空間須要所有按照上方的方法進行動態計算。。因此也是比較坑爹的。。
拿了網上一個畫廊的效果。直接
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最開始是徹底透明的。而且跟隨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);
}
}
讓header和RecyclerView關聯起來的就是Behavior了。Behavior以前寫過幾篇介紹過了,這裏就再也不囉嗦。
denpendcy爲HeaderView。而且監聽RecyclerView的滑動。
具體的細節仍是看源碼吧~
若是你以爲還不錯,歡迎Star
本項目地址
歡迎加入個人qq羣: 425983695