好久沒有寫文章了歧蒋,慢慢的自己工作中遇到的問題不做積累,下次遇到還會忘州既。哎谜洽。。吴叶。阐虚。
周日無聊的單身程序員-唯有程序你懂的...
寫著程序聽著歌也是極好的!晤郑!
最近工作中要實現(xiàn)類似 今日頭條等新聞類APP頂部導航條的效果 敌呈,不過我們這效果切換時要加上文字顏色的漸變和縮放。
git圖片比較大造寝,耐心等待哦磕洪,小寶貝~~
一:分析
今天我們要實現(xiàn)這種特效。
用到的開源項目有:master-nineoldandroids-library.jar這個jar包诫龙,這個是向下兼容的jar包析显,包括android一系列的動畫。
首先我們說一下這種 日頭條等新聞類APP 的基本實現(xiàn)是ViewPage+Fragment+HorizontalScrollView
我們今天說的就是這個HorizontalScrollView的特效签赃。
實現(xiàn)原理圖:
相信大家已經(jīng)明白了大概
就是:最初時我們 初始化textview 并把選中的和正常的textview 初始化谷异。就是上圖中的framelayout中的2個textview 放到集合 HashMap<String, View>()中。用于保持所有textview的狀態(tài)锦聊。
至于代碼怎么寫呢歹嘹?!
android系統(tǒng)給我們提供了一個叫PagerSlidingTabStrip的類孔庭,在v4包中尺上。我們把java中考出來放到我們的項目中,修改其中的代碼就可以圆到。
我們通過viewpage來控制導航條怎抛。把viewpage傳到PagerSlidingTabStrip中,并設置監(jiān)聽器芽淡,代碼如下:
public void setViewPager(ViewPager pager) {
this.pager = pager;
if (pager.getAdapter() == null) {
throw new IllegalStateException(
"ViewPager does not have adapter instance.");
}
pager.setOnPageChangeListener(pageListener);
notifyDataSetChanged();
}
二马绝、獲取用戶切換時當前View和切換至的目的View。
ViewPager也需要監(jiān)聽用戶的手勢挣菲,所以肯定提供了某個方法富稻。于是縱觀ViewPager的方法掷邦,發(fā)現(xiàn)了一個叫做 onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的方法~~
沒錯就是這個方法:在頁面滾動時調(diào)用~
下面仔細研究下這幾個參數(shù):
直接說測試結(jié)果:
在非第一頁與最后一頁時,滑動到下一頁唉窃,position為當前頁位置耙饰;滑動到上一頁:position為當前頁-1
positionOffset 滑動到下一頁纹笼,[0,1)區(qū)間上變化纹份;滑動到上一頁:(1,0]區(qū)間上變化
positionOffsetPixels這個和positionOffset很像:滑動到下一頁,[0,寬度)區(qū)間上變化廷痘;滑動到上一頁:(寬度,0]區(qū)間上變化
第一頁時:滑動到上一頁position=0 蔓涧,其他基本為0 ;最后一頁滑動到下一頁 position為當前頁位置笋额,其他兩個參數(shù)為0
豁然發(fā)現(xiàn)元暴,我們需要的步驟的第二步解決了,positionOffset很適合作為兄猩,漸變茉盏,縮放的控制參數(shù);positionOffsetPixels則可以作為平移等的控制參數(shù)枢冤。
那么如何獲得當前View和目的View呢:
分享幾個我的歧途:
1鸠姨、【錯誤】我通過getChildAt(position),getChildAt(position+1)淹真,getChildAt(position-1)獲得滑動時讶迁,左右的兩個View;乍一看核蘸,還真覺得不錯~~在代碼寫出來巍糯,再乍效果也出不來錯誤原因:我們忽略一個特別大的東西,ViewPager的機制客扎,滑動時動態(tài)加載和刪除View祟峦,ViewPager其實只會維持2到3個View,而position的范圍基本屬于無限~
2徙鱼、【錯誤】我通過getCurrentItem獲得當前的位置宅楞,然后+1,-1獲得后一個或者前一個正在竊喜疆偿,趕快代碼改過來咱筛,效果怎么也不對,亂七八糟的仔細觀察日志杆故,這個getCurrentItem當用戶手指離開的屏幕迅箩,Page還在動畫執(zhí)行時,就改變了難怪~整個滑動過程并不是固定的唉处铛,心都碎了~
3饲趋、【錯誤】position在整個滑動的過程中是不變化的拐揭,而且ViewPager會保存2個或3個View;那么我考慮,如果是第一頁奕塑、或者最后一頁那么我取getChildAt(0)和getChildAt(1)堂污,如果在其他頁面則為getChildAt(0),getChildAt(2),然后經(jīng)過一系列的變化~我想這會總該對了吧,于是我遇到第一問題龄砰,第一頁的時候盟猖,不管左右position都為0,尼瑪换棚,這哪個為左View式镐,哪個為右View~~
說了這么多錯誤,大家可以繞過這些彎路固蚤,也能從這些彎路里面看出點什么~
下面說正確的娘汞,其實ViewPager在添加一個View或者銷毀一個View時,是我們自己的PageAdapter中控制的夕玩,于是我們可以在ViewPager里面維系一個HashMap<Position你弦,View>,然后滑動的時候燎孟,通過get(position)取出禽作,比如上述效果,始終是右邊的View變化缤弦,要么從小到大领迈,要么從大到小
那么滑倒下一頁:左邊的View:map.get(position) ,右邊的View : map.get(position+1) .
那么滑倒上一頁:左邊的View : map.get(position) 碍沐, 右邊的View : map.get(position+1) 狸捅, 一樣的,因為滑到上一頁累提,position為當前頁-1
關(guān)鍵代碼:
<pre name="code" class="java"> private class PageListener implements OnPageChangeListener {
private int oldPosition = 0;
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
currentPosition = position;
currentPositionOffset = positionOffset;
scrollToChild(position, (int) (positionOffset * tabsContainer
.getChildAt(position).getWidth()));
invalidate();
if (delegatePageListener != null) {
delegatePageListener.onPageScrolled(position, positionOffset,
positionOffsetPixels);
}
if (mState == State.IDLE && positionOffset > 0) {
oldPage = pager.getCurrentItem();
mState = position == oldPage ? State.GOING_RIGHT
: State.GOING_LEFT;
}
boolean goingRight = position == oldPage;
if (mState == State.GOING_RIGHT && !goingRight)
mState = State.GOING_LEFT;
else if (mState == State.GOING_LEFT && goingRight)
mState = State.GOING_RIGHT;
float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;
View mLeft = tabsContainer.getChildAt(position);
View mRight = tabsContainer.getChildAt(position + 1);
if (effectOffset == 0) {
mState = State.IDLE;
}
if (mFadeEnabled)
animateFadeScale(mLeft, mRight, effectOffset, position);
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
scrollToChild(pager.getCurrentItem(), 0);
mFadeEnabled = true;
}
if (delegatePageListener != null) {
delegatePageListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageSelected(int position) {
// selectedPosition = position;
// updateTabStyles();
currentPosition = position;
// set old view statue
ViewHelper.setAlpha(tabViews.get(oldPosition).get("normal"), 1);
ViewHelper.setAlpha(tabViews.get(oldPosition).get("selected"), 0);
View v_old = tabsContainer.getChildAt(oldPosition);
ViewHelper.setPivotX(v_old, v_old.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(v_old, v_old.getMeasuredHeight() * 0.5f);
ViewHelper.setScaleX(v_old, 1f);
ViewHelper.setScaleY(v_old, 1f);
// set new view statue
ViewHelper.setAlpha(tabViews.get(position).get("normal"), 0);
ViewHelper.setAlpha(tabViews.get(position).get("selected"), 1);
View v_new = tabsContainer.getChildAt(position);
ViewHelper.setPivotX(v_new, v_new.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(v_new, v_new.getMeasuredHeight() * 0.5f);
ViewHelper.setScaleX(v_new, 1 + ZOOM_MAX);
ViewHelper.setScaleY(v_new, 1 + ZOOM_MAX);
if (delegatePageListener != null) {
delegatePageListener.onPageSelected(position);
}
// oldPosition = selectedPosition;
oldPosition = currentPosition;
}
}
可以看到代碼:縮放view很關(guān)鍵:
View v_old = tabsContainer.getChildAt(oldPosition);
ViewHelper.setPivotX(v_old, v_old.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(v_old, v_old.getMeasuredHeight() * 0.5f);
ViewHelper.setScaleX(v_old, 1f);
ViewHelper.setScaleY(v_old, 1f);
找到view的中心點即:setPivotX setPivotY 然后對X軸Y軸縮放尘喝。1為原始大小。>1放大斋陪,<1 且>0 縮小朽褪。
關(guān)鍵代碼是:onPageScrolled 方法的底2個參數(shù)positionOffset 滑動的百分比。
漸變通過animateFadeScale這個方法控制:
protected void animateFadeScale(View left, View right,
float positionOffset, int position) {
if (mState != State.IDLE) {
if (left != null) {
ViewHelper.setAlpha(tabViews.get(position).get("normal"),
positionOffset);
ViewHelper.setAlpha(tabViews.get(position).get("selected"),
1 - positionOffset);
float mScale = 1 + ZOOM_MAX - ZOOM_MAX * positionOffset;
ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);
ViewHelper.setScaleX(left, mScale);
ViewHelper.setScaleY(left, mScale);
}
if (right != null) {
ViewHelper.setAlpha(tabViews.get(position + 1).get("normal"),
1 - positionOffset);
ViewHelper.setAlpha(tabViews.get(position + 1).get("selected"),
positionOffset);
float mScale = 1 + ZOOM_MAX * positionOffset;
ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);
ViewHelper.setScaleX(right, mScale);
ViewHelper.setScaleY(right, mScale);
}
}
}
在你activity/引用的地方 中你可以直接設置TAB的顏色大小无虚,正常色缔赠,選中色等。EG:
/**
* 對PagerSlidingTabStrip的各項屬性進行賦值友题。
*/
private void setTabsValue() {
// 設置Tab是自動填充滿屏幕的
tabs.setShouldExpand(true);
// 設置Tab的分割線是透明的
tabs.setDividerColor(Color.TRANSPARENT);
// 設置Tab底部線的高度
tabs.setUnderlineHeight((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 1, dm));
// 設置Tab Indicator的高度
tabs.setIndicatorHeight((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 4, dm));
// 設置Tab標題文字的大小
tabs.setTextSize((int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, dm));
// 設置Tab Indicator的顏色
tabs.setIndicatorColor(Color.parseColor("#45c01a"));
// 設置選中Tab文字的顏色 (這是我自定義的一個方法)
tabs.setSelectedTextColor(Color.parseColor("#45c01a"));
//設置正常Tab文字的顏色 (這是我自定義的一個方法)
tabs.setTextColor(Color.parseColor("#C231C7"));
// 取消點擊Tab時的背景色
tabs.setTabBackground(0);
}
源碼:
可以使用依賴庫嗤堰,github上都有說明
github地址:
https://github.com/ta893115871/PagerSlidingTabStrip/
記得start哦