Android實(shí)現(xiàn)知乎選項(xiàng)卡動(dòng)態(tài)隱藏效果實(shí)例
前言
因?yàn)樽罱稚享?xiàng)目也是資訊閱讀類,簡(jiǎn)書,掘金,知乎的效果都想往項(xiàng)目上加,沒事就來仿寫。

選項(xiàng)卡動(dòng)態(tài)隱藏.gif
效果呢,和知乎首頁一樣,可以去知乎看看;點(diǎn)擊back鍵可以返回頂部。下面話不多說了,來一起看看詳細(xì)的介紹吧。
想法:
- 列表上拉,選項(xiàng)卡隱藏,下滑出現(xiàn);recycleView滾動(dòng)監(jiān)聽(OnScrollListener)中onScrolled方法的dy參數(shù),dy>0表示上拉,dy<0表示下滑,剛好合適。
- 選項(xiàng)卡怎么隱藏呢,屬性動(dòng)畫,移動(dòng)選項(xiàng)卡的相對(duì)位置View.TRANSLATION_Y(Y軸方向移動(dòng)肯定是_Y),View.TRANSLATION系列都是相對(duì)運(yùn)動(dòng),參考系是view原本的位置。
- 還有個(gè)問題,對(duì)于選項(xiàng)卡來說,它需要的顯隱時(shí)機(jī)是列表滑動(dòng)方向改變,而不是只監(jiān)聽它的滑動(dòng);上拉改下滑,下滑改上拉這2個(gè)時(shí)機(jī)才能執(zhí)行動(dòng)畫,不能在列表同一方向持續(xù)滾動(dòng)時(shí)重復(fù)調(diào)用動(dòng)畫。
步驟:
要寫多少代碼呢? fragmeng中一個(gè)recycleView的監(jiān)聽要寫,一個(gè)接口要寫;activity中接口實(shí)現(xiàn)。沒了,代碼不多。
Fragment:
public interface RvScrollListener {
//滑動(dòng)方向監(jiān)聽
void scrollType(boolean direction);
//是否滑動(dòng)到頂部監(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) {
//如果不是第一個(gè)fragment則返回
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
//得到當(dāng)前列表第一個(gè)完全顯示的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);
//判斷滑動(dòng)方向,recycleView item 上拉 下滑不同動(dòng)畫
if (dy > 0) {
isUp = true;
} else {
isUp = false;
}
if (fragmentposition != 0) {
return;
//如果不是第一個(gè)fragment則返回
}
//過濾掉一些緩慢的滑動(dòng)
if (Math.abs(dy) > 10) {
//滑動(dòng)方向
mRvScrollListener.scrollType(dy > 0);
}
}
};
recycleView第一個(gè)監(jiān)聽方法:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {}
這個(gè)里面就做一件事情,判斷當(dāng)前recycleView是否滑動(dòng)到頂部,然后通過接口傳遞到activity中,當(dāng)點(diǎn)擊back鍵時(shí),如果不在頂部,則調(diào)用方法滾動(dòng)到頂部。
recycleView第二個(gè)監(jiān)聽方法:
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {}
做2件事,一是recyleView的item做動(dòng)畫時(shí),因?yàn)樯侠拖禄瑒?dòng)畫不一樣,代碼中 isUp 就是用來區(qū)分上拉下滑的((給recycleView的item做加載動(dòng)畫使用));
二是判斷滑動(dòng)方向,接口傳遞到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) {
//點(diǎn)擊返回鍵
if (keyCode == KeyEvent.KEYCODE_BACK) {
//如果當(dāng)前不是第一個(gè)fragmeng則顯示第一個(gè)
if (mViewPager.getCurrentItem() != 0) {
mViewPager.setCurrentItem(0);
return true;
}
//如果當(dāng)前recycleView沒有在頂部則返回頂部
if (!inTop) {
topRecyclerView.smoothScrollToPosition(0);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
實(shí)現(xiàn)接口第一個(gè)方法:
@Override
public void scrollType(boolean direction) {}
方法里用到三個(gè)boolean值 direction ,hasup, hasdown ,direction判斷執(zhí)行上拉動(dòng)畫或者下滑動(dòng)畫;hasup和hasdown作用是:滑動(dòng)有上拉,下滑2個(gè)狀態(tài),處于一種狀態(tài)時(shí)動(dòng)畫只執(zhí)行一次;比如列表正在持續(xù)上拉,監(jiān)聽也會(huì)觸發(fā)多次,上拉的多次觸動(dòng)中只執(zhí)行一次動(dòng)畫。
實(shí)現(xiàn)接口第二個(gè)方法:
@Override
public void inTop(boolean top, RecyclerView recyclerView) {}
就一個(gè)賦值作用,用在back鍵的點(diǎn)擊事件中onKeyDown。
back鍵點(diǎn)擊
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {}
一點(diǎn)需要注意:recycleView滾動(dòng)到頂部,調(diào)用的是smoothScrollToPosition()方法,這個(gè)最簡(jiǎn)單,調(diào)用別的方法譬如smoothScrollBy() ,還需要算距離,不過這個(gè)方法可以給插值器。
屬性動(dòng)畫
//隱藏 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個(gè)點(diǎn),一個(gè)是 View.TRANSLATION_Y 這個(gè)參數(shù)要寫對(duì),
另外一個(gè)是動(dòng)畫的起始值:
如果隱藏動(dòng)畫是從0dp移動(dòng)到50dp,快速切換上拉下滑狀態(tài)時(shí)(手指快速上下滑動(dòng))控件就會(huì)閃。所以隱藏動(dòng)畫中從 mTablayout.getTranslationY()的位置移動(dòng)到 50 dp的位置,動(dòng)態(tài)獲取當(dāng)前選項(xiàng)卡位置就好了,顯示動(dòng)畫同理。(寫50dp是因?yàn)槲疫x項(xiàng)卡高度就是50dp)
另外把recycleView的item加載動(dòng)畫代碼給出來:(這個(gè)寫在Adapter里面的,因?yàn)橐趏nBindViewHolder時(shí)調(diào)用)
protected Animator[] getAnimators(View view) { //上滑動(dòng)畫
return new Animator[]{
ObjectAnimator.ofFloat(view, View.ROTATION, 120, 0).setDuration(400)
};
}
protected Animator[] getAnimatorsDown(View view) { //下拉動(dòng)畫
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加載動(dòng)畫和選項(xiàng)卡顯隱動(dòng)畫差不多,你可以把 View.SCALE_X 這種參數(shù)改一改,多試試效果,注意起始值。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Android實(shí)現(xiàn)底部導(dǎo)航欄功能(選項(xiàng)卡)
- Android多個(gè)TAB選項(xiàng)卡切換效果
- Android仿微信底部實(shí)現(xiàn)Tab選項(xiàng)卡切換效果
- Android利用Fragment實(shí)現(xiàn)Tab選項(xiàng)卡效果
- Android實(shí)現(xiàn)類似網(wǎng)易新聞選項(xiàng)卡動(dòng)態(tài)滑動(dòng)效果
- Android編程實(shí)現(xiàn)自定義Tab選項(xiàng)卡功能示例
- Android編程實(shí)現(xiàn)將tab選項(xiàng)卡放在屏幕底部的方法
- Android開發(fā)之選項(xiàng)卡功能的實(shí)現(xiàn)方法示例
相關(guān)文章
更新Android Studio 3.0碰到的問題小結(jié)
本文是小編給大家分享的更新Android Studio 3.0碰到的問題小結(jié),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-11-11
Moshi?完美解決Gson在kotlin中默認(rèn)值空的問題詳解
這篇文章主要為大家介紹了Moshi?完美解決Gson在kotlin中默認(rèn)值空的問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Android?手寫RecyclerView實(shí)現(xiàn)列表加載
這篇文章主要介紹了Android?手寫RecyclerView實(shí)現(xiàn)列表加載,涉及到列表的需求,肯定第一時(shí)間想到RecyclerView,即便是自定義View,那么RecyclerView也會(huì)是首選,為什么會(huì)選擇RecyclerView而不是ListView,主要就是RecyclerView的內(nèi)存復(fù)用機(jī)制,這也是RecyclerView的核心?2022-08-08
Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
Android開發(fā)之電話撥號(hào)器和短信發(fā)送器實(shí)現(xiàn)方法
這篇文章主要介紹了Android開發(fā)之電話撥號(hào)器和短信發(fā)送器實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android電話撥號(hào)器和短信發(fā)送器的具體原理與實(shí)現(xiàn)步驟,需要的朋友可以參考下2015-12-12
Android使用MediaRecorder實(shí)現(xiàn)錄像功能
這篇文章主要為大家詳細(xì)介紹了Android使用MediaRecorder實(shí)現(xiàn)錄像功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06

