效果呢族吻,和知乎首頁一樣,可以去知乎看看哗蜈;點擊back鍵可以返回頂部纽竣。
想法:
- 列表上拉,選項卡隱藏储笑,下滑出現(xiàn)甜熔;recycleView滾動監(jiān)聽(OnScrollListener)中onScrolled方法的dy參數(shù),dy>0表示上拉突倍,dy<0表示下滑腔稀,剛好合適盆昙。
- 選項卡怎么隱藏呢,屬性動畫焊虏,移動選項卡的相對位置View.TRANSLATION_Y(Y軸方向移動肯定是_Y),View.TRANSLATION系列都是相對運(yùn)動淡喜,參考系是view原本的位置。
- 還有個問題诵闭,對于選項卡來說炼团,它需要的顯隱時機(jī)是列表滑動方向改變,而不是只監(jiān)聽它的滑動疏尿;上拉改下滑瘟芝,下滑改上拉這2個時機(jī)才能執(zhí)行動畫,不能在列表同一方向持續(xù)滾動時重復(fù)調(diào)用動畫褥琐。
步驟:
要寫多少代碼呢锌俱? fragmeng中一個recycleView的監(jiān)聽要寫,一個接口要寫踩衩;activity中接口實現(xiàn)嚼鹉。沒了,代碼不多驱富。
Fragment:
public interface RvScrollListener {
//滑動方向監(jiān)聽
void scrollType(boolean direction);
//是否滑動到頂部監(jiān)聽
void inTop(boolean top,RecyclerView recyclerView);
}
private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (fragmentposition != 0) {
//如果不是第一個fragment則返回
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
//得到當(dāng)前列表第一個完全顯示的item的position
int position = layoutManager.findFirstCompletelyVisibleItemPosition();
if (position == 0) {
//如果position為0表示列表正處于頂部
mRvScrollListener.inTop(true, recyclerView);
} else {
mRvScrollListener.inTop(false, recyclerView);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//判斷滑動方向锚赤,recycleView item 上拉 下滑不同動畫
if (dy > 0) {
isUp = true;
} else {
isUp = false;
}
if (fragmentposition != 0) {
return;
//如果不是第一個fragment則返回
}
//過濾掉一些緩慢的滑動
if (Math.abs(dy) > 10) {
//滑動方向
mRvScrollListener.scrollType(dy > 0);
}
}
};
- recycleView第一個監(jiān)聽方法:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {}
這個里面就做一件事情,判斷當(dāng)前recycleView是否滑動到頂部褐鸥,然后通過接口傳遞到activity中线脚,當(dāng)點擊back鍵時,如果不在頂部叫榕,則調(diào)用方法滾動到頂部浑侥。
- recycleView第二個監(jiān)聽方法:
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {}
做2件事,一是recyleView的item做動畫時晰绎,因為上拉和下滑動畫不一樣寓落,代碼中 isUp 就是用來區(qū)分上拉下滑的((給recycleView的item做加載動畫使用));
二是判斷滑動方向荞下,接口傳遞到activity中伶选。
Activity:
//上拉狀態(tài)
private boolean hasup = true;
//下滑狀態(tài)
private boolean hasdown = true;
//是否在頂部
private boolean inTop = true;
//從fragment傳遞過來的recycleView
private RecyclerView topRecyclerView;
BlankFragment.RvScrollListener mRvScrollListener = new BlankFragment.RvScrollListener() {
@Override
public void scrollType(boolean direction) {
//上拉
if (direction) {
hasdown = true;
//連續(xù)上拉,第一次有效
if (hasup) {
ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), PixelChange.dp2px(XjwTablayoutActivity.this, 50)).setDuration(400).start();
hasup = false;
}
} else {//下滑
hasup = true;
//連續(xù)下滑尖昏,第一次有效
if (hasdown) {
ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), 0).setDuration(400).start();
hasdown = false;
}
}
}
@Override
public void inTop(boolean top, RecyclerView recyclerView) {
inTop = top;
topRecyclerView = recyclerView;
}
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//點擊返回鍵
if (keyCode == KeyEvent.KEYCODE_BACK) {
//如果當(dāng)前不是第一個fragmeng則顯示第一個
if (mViewPager.getCurrentItem() != 0) {
mViewPager.setCurrentItem(0);
return true;
}
//如果當(dāng)前recycleView沒有在頂部則返回頂部
if (!inTop) {
topRecyclerView.smoothScrollToPosition(0);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
- 實現(xiàn)接口第一個方法:
@Override
public void scrollType(boolean direction) {}
方法里用到三個boolean值 direction ,hasup, hasdown 仰税,direction判斷執(zhí)行上拉動畫或者下滑動畫;hasup和hasdown作用是:滑動有上拉抽诉,下滑2個狀態(tài)陨簇,處于一種狀態(tài)時動畫只執(zhí)行一次;比如列表正在持續(xù)上拉迹淌,監(jiān)聽也會觸發(fā)多次河绽,上拉的多次觸動中只執(zhí)行一次動畫己单。
- 實現(xiàn)接口第二個方法:
@Override
public void inTop(boolean top, RecyclerView recyclerView) {}
就一個賦值作用,用在back鍵的點擊事件中onKeyDown葵姥。
- back鍵點擊
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {}
一點需要注意:recycleView滾動到頂部荷鼠,調(diào)用的是smoothScrollToPosition()方法,這個最簡單榔幸,調(diào)用別的方法譬如smoothScrollBy()允乐,還需要算距離,不過這個方法可以給插值器削咆。
- 屬性動畫
//隱藏
ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), PixelChange.dp2px(XjwTablayoutActivity.this, 50)).setDuration(400).start();
//顯示
ObjectAnimator.ofFloat(mTablayout, View.TRANSLATION_Y, mTablayout.getTranslationY(), 0).setDuration(400).start();
注意2個點牍疏,一個是 View.TRANSLATION_Y 這個參數(shù)要寫對,
另外一個是動畫的起始值:
如果隱藏動畫是從0dp移動到50dp,快速切換上拉下滑狀態(tài)時(手指快速上下滑動)控件就會閃拨齐。所以隱藏動畫中從 mTablayout.getTranslationY()的位置移動到 50 dp的位置鳞陨,動態(tài)獲取當(dāng)前選項卡位置就好了,顯示動畫同理瞻惋。(寫50dp是因為我選項卡高度就是50dp)
另外把recycleView的item加載動畫代碼給出來:(這個寫在Adapter里面的,因為要在onBindViewHolder時調(diào)用)
protected Animator[] getAnimators(View view) { //上滑動畫
return new Animator[]{
ObjectAnimator.ofFloat(view, View.ROTATION, 120, 0).setDuration(400)
};
}
protected Animator[] getAnimatorsDown(View view) { //下拉動畫
return new Animator[]{
ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -100, 0).setDuration(400),
ObjectAnimator.ofFloat(view, View.SCALE_X, 0.7f, 1f).setDuration(400)
};
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isUp) { //上拉
Animator[] animators = getAnimators(holder.itemView);
if (animators.length > 1) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animators);
animatorSet.start();
} else {
for (Animator annimator : animators) {
annimator.start();
}
}
} else {//下拉
Animator[] animatorsDown = getAnimatorsDown(holder.itemView);
if (animatorsDown.length > 1) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animatorsDown);
animatorSet.start();
} else {
for (Animator annimator : animatorsDown) {
annimator.start();
}
}
}
}
item加載動畫和選項卡顯隱動畫差不多厦滤,你可以把 View.SCALE_X 這種參數(shù)改一改,多試試效果歼狼,注意起始值掏导。
對于生活理想,應(yīng)該像宗教徒對待宗教一樣充滿虔誠與熱情羽峰!