Android仿餓了么詳情頁(yè)可以跟隨手指移動(dòng) viewpager變?cè)斍轫?yè)

源碼地址[https://github.com/githubwing/ZoomHeader]


不是共享元素!不是共享元素!不是共享元素!重要的話說(shuō)三遍。共享元素不可以隨手指移動(dòng)的。
先吐槽下餓了么。不提示左右可以滑動(dòng)。我還是無(wú)意中發(fā)現(xiàn)的。不提示我怎么知道可以滑動(dòng)??
這是一個(gè)模仿餓了么詳情頁(yè)的例子。并非一個(gè)庫(kù),并非拿來(lái)就可以用,主要講解思路以及如何實(shí)現(xiàn),可能有一些細(xì)節(jié)沒(méi)有處理。 講述了如何實(shí)現(xiàn)。具體祥見(jiàn)源碼。
他是一個(gè)Activity還是兩個(gè)?
相信你肯定有這樣的疑問(wèn),答案是一個(gè)。所以這不是用共享元素實(shí)現(xiàn)的,使用共享元素會(huì)導(dǎo)致圖片無(wú)法跟隨手指移動(dòng)。
你看到的中間imageview是viewpager。在Viewpager上面是一個(gè)透明的View。當(dāng)然,這個(gè)Activity的背景也是透明的。
實(shí)現(xiàn)思路
我使用CoordinatorLayout+Behavior實(shí)現(xiàn)的。說(shuō)實(shí)話,Behavior真心強(qiáng)大。。
viewpager+頭部
整個(gè)實(shí)現(xiàn)的思路是這樣的。整體布局從上到下依次是:
透明View
viewpager
RecyclerView

其中透明View和Viewpager 合并成一個(gè)自定義的Header。當(dāng)這個(gè)Header上移的時(shí)候,圖片放大,并且RecyclerView聯(lián)動(dòng)上衣,從透明轉(zhuǎn)向并且不透明。
所以首先要定制一個(gè)透明的可移動(dòng)的HeaderView。
在onTouchEvent處理一下手勢(shì)。。

@Override public boolean onTouchEvent(MotionEvent ev) { 
switch (ev.getAction()) { 
case MotionEvent.ACTION_DOWN:
 return true; 
case MotionEvent.ACTION_MOVE: 
if(上下移動(dòng)到閥值){            展開(kāi)為詳情()      
}else if(上下滑動(dòng)到閥值,恢復(fù)viewpager){       
}else if(下滑,則關(guān)閉Activity)

將header分為三種狀態(tài):
上移。則展開(kāi)為詳情頁(yè)。
下移,則恢復(fù)為viewpager。
再下移,則finish Activity。

在上移的過(guò)程中,遇到了一點(diǎn)小挑戰(zhàn),這里分享下:
上移的過(guò)程中,圖片需要放大。但是在做的過(guò)程中,不能使用LayoutParams實(shí)現(xiàn)。這里就關(guān)系到一些動(dòng)畫(huà)的小細(xì)節(jié)。
動(dòng)畫(huà)使用LayoutParams實(shí)現(xiàn)是一個(gè)禁忌。他會(huì)導(dǎo)致不停requestLayout,從而影響UI性能。
所以這里我的一個(gè)解法就是,我放大圖片,不是真正的改變ImageView大小,而是去Scale圖片。即使看起來(lái)變大了,他的View真正大小也不會(huì)變。
所以,有一句話叫做真亦是假、假亦是真 真真假假,你又何必當(dāng)真呢?動(dòng)畫(huà)效果只要遵循這句話,基本上都是可以實(shí)現(xiàn)的。你所看到的效果都是假的。都是障眼法。View變大不是真正的變大。View懸浮不是真正的懸浮(有可能是顯隱)。就像變魔術(shù)一樣。。其實(shí)很簡(jiǎn)單。
接下來(lái)又遇到問(wèn)題了。圖片放大了,文字如何對(duì)齊? 文字的位置當(dāng)然也不能真正改變。所以這里使用TranslationX實(shí)現(xiàn)。在圖片放大的過(guò)程中,使用scale的系數(shù),與兩個(gè)端點(diǎn)值進(jìn)行一個(gè)線性變化計(jì)算。主要文字對(duì)齊代碼如下:

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

第二個(gè)點(diǎn)。就是在圖片放大過(guò)程中,底部文字和按鈕左右padding不能變。這也是我沒(méi)有封裝成一個(gè)拿來(lái)就用的View的原因(其實(shí)還是水平不夠)。因?yàn)檫@些空間需要全部按照上方的方法進(jìn)行動(dòng)態(tài)計(jì)算。。所以也是比較坑爹的。。
ViewPager
拿了網(wǎng)上一個(gè)畫(huà)廊的效果。直接

setPageTransformer(true, new ZoomOutPageTransformer());

這里注意,需要改變一下view的繪制順序,保證當(dāng)前view是最后繪制處于最上層

/改變系統(tǒng)繪制順序
 @Override protected int getChildDrawingOrder(int childCount, int i) { 
int position = getCurrentItem(); 
if(position<0){
 return i; 
}else{ if(i == childCount - 1){//這是最后一個(gè)需要刷新的item 
if(position>i){ 
position=i; } 
return position; } 
if(i == position){//這是原本要在最后一個(gè)刷新的item return childCount - 1; } } 
return i; }}

RecyclerView
RecyclerView最開(kāi)始是完全透明的。并且跟隨HeaderView上移而上移,在上移的過(guò)程中漸漸顯示出來(lái)。 需要監(jiān)聽(tīng)RecyclerView滾動(dòng),當(dāng)RecyclerView滾動(dòng)到頂部的時(shí)候。告知Header,該恢復(fù)最初原樣了。

@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) { //向下滑動(dòng) 
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關(guān)聯(lián)起來(lái)的就是Behavior了。Behavior之前寫(xiě)過(guò)幾篇介紹過(guò)了,這里就不再啰嗦。
denpendcy為HeaderView。并且監(jiān)聽(tīng)RecyclerView的滑動(dòng)。
具體的細(xì)節(jié)還是看源碼吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,180評(píng)論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,169評(píng)論 22 665
  • “黑魔大澤這次血祭失敗,灰溜溜離去,痛快痛快啊?!贝居劬补Φ?。 一旁的貪鵬帝君卻笑瞇瞇的:“應(yīng)山雪鷹之前一...
    im喵小姐閱讀 474評(píng)論 0 0
  • 趕路再次路過(guò)此地,有偶遇,卻缺憾。
    一念歸遠(yuǎn)閱讀 148評(píng)論 0 0
  • 新茶清挈,望楓林晚,煙雨方歇。大河遠(yuǎn)山無(wú)續(xù),才抬望眼、春風(fēng)催發(fā)。楊柳新折殘?jiān)?,正清明時(shí)節(jié)。爾悠悠、十年滄海,多少新...
    楓林路植物人閱讀 306評(píng)論 0 1

友情鏈接更多精彩內(nèi)容