Android-導航欄特效-新聞類APP(仿iOS版網易新聞今日頭條的文字漸變縮放特效)
週日無聊的單身程式設計師-唯有程式你懂的... 寫著程式聽著歌也是極好的!!
最近工作中要實現類似 今日頭條等新聞類APP頂部導航條的效果 ,不過我們這效果切換時要加上文字顏色的漸變和縮放。 git圖片比較大,耐心等待哦,小寶貝~~
一:分析
今天我們要實現這種特效。
用到的開源專案有:master-nineoldandroids-library.jar這個jar包,這個是向下相容的jar包,包括android一系列的動畫。
首先我們說一下這種 日頭條等新聞類APP 的基本實現是ViewPage+Fragment+HorizontalScrollView
我們今天說的就是這個HorizontalScrollView的特效。
實現原理圖:
相信大家已經明白了大概
就是:最初時我們 初始化textview 並把選中的和正常的textview 初始化。就是上圖中的framelayout中的2個textview 放到集合 HashMap<String, View>()中。用於保持所有textview的狀態。
至於程式碼怎麼寫呢?!
android系統給我們提供了一個叫PagerSlidingTabStrip的類,在v4包中。我們把java中考出來放到我們的專案中,修改其中的程式碼就可以。
我們通過viewpage來控制導航條。把viewpage傳到PagerSlidingTabStrip中,並設定監聽器,程式碼如下:
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也需要監聽使用者的手勢,所以肯定提供了某個方法。於是縱觀ViewPager的方法,發現了一個叫做 onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的方法~~
沒錯就是這個方法:在頁面滾動時呼叫~
下面仔細研究下這幾個引數:
直接說測試結果:
在非第一頁與最後一頁時,滑動到下一頁,position為當前頁位置;滑動到上一頁:position為當前頁-1
positionOffset 滑動到下一頁,[0,1)區間上變化;滑動到上一頁:(1,0]區間上變化
positionOffsetPixels這個和positionOffset很像:滑動到下一頁,[0,寬度)區間上變化;滑動到上一頁:(寬度,0]區間上變化
第一頁時:滑動到上一頁position=0 ,其他基本為0 ;最後一頁滑動到下一頁 position為當前頁位置,其他兩個引數為0
豁然發現,我們需要的步驟的第二步解決了,positionOffset很適合作為,漸變,縮放的控制引數;positionOffsetPixels則可以作為平移等的控制引數。
那麼如何獲得當前View和目的View呢:
分享幾個我的歧途:
1、【錯誤】我通過getChildAt(position),getChildAt(position+1),getChildAt(position-1)獲得滑動時,左右的兩個View;乍一看,還真覺得不錯~~~在程式碼寫出來,再乍效果也出不來~~錯誤原因:我們忽略一個特別大的東西,ViewPager的機制,滑動時動態載入和刪除View,ViewPager其實只會維持2到3個View,而position的範圍基本屬於無限~~
2、【錯誤】我通過getCurrentItem獲得當前的位置,然後+1,-1獲得後一個或者前一個~~正在竊喜,趕快程式碼改過來,效果怎麼也不對,亂七八糟的~~仔細觀察日誌,這個getCurrentItem當用戶手指離開的螢幕,Page還在動畫執行時,就改變了~~難怪~整個滑動過程並不是固定的~~唉,心都碎了~
3、【錯誤】position在整個滑動的過程中是不變化的,而且ViewPager會儲存2個或3個View;那麼我考慮,如果是第一頁、或者最後一頁那麼我取getChildAt(0)和getChildAt(1),如果在其他頁面則為getChildAt(0),getChildAt(2),然後經過一系列的變化~我想這會總該對了吧,於是我遇到第一問題,第一頁的時候,不管左右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
關鍵程式碼:
<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很關鍵:
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 縮小。
關鍵程式碼是:onPageScrolled 方法的底2個引數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地址哦,免費的,不要資源積
分。程式碼已經沒有多餘的類了。也可以直接食用依賴庫,github上都有說明哦!
不要忘記start哦!!
csdn資源-原始碼