Android-導航欄特效-新聞類APP(仿iOS版網(wǎng)易新聞今日頭條的文字漸變縮放特效)

好久沒有寫文章了歧蒋,慢慢的自己工作中遇到的問題不做積累,下次遇到還會忘州既。哎谜洽。。吴叶。阐虚。

周日無聊的單身程序員-唯有程序你懂的...
寫著程序聽著歌也是極好的!晤郑!

最近工作中要實現(xiàn)類似 今日頭條等新聞類APP頂部導航條的效果 敌呈,不過我們這效果切換時要加上文字顏色的漸變和縮放。
git圖片比較大造寝,耐心等待哦磕洪,小寶貝~~

![1.gif](http://upload-images.jianshu.io/upload_images/442437-37ce63f0ec56f0fd.gif?imageMogr2/auto-orient/strip)
s4.gif

一:分析
今天我們要實現(xiàn)這種特效。
用到的開源項目有:master-nineoldandroids-library.jar這個jar包诫龙,這個是向下兼容的jar包析显,包括android一系列的動畫。
首先我們說一下這種 日頭條等新聞類APP 的基本實現(xiàn)是ViewPage+Fragment+HorizontalScrollView
我們今天說的就是這個HorizontalScrollView的特效签赃。
實現(xiàn)原理圖:

20150329110204358.png

相信大家已經(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哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市度宦,隨后出現(xiàn)的幾起案子踢匣,更是在濱河造成了極大的恐慌告匠,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件离唬,死亡現(xiàn)場離奇詭異后专,居然都是意外死亡,警方通過查閱死者的電腦和手機输莺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門戚哎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人模闲,你說我怎么就攤上這事建瘫≌负矗” “怎么了尸折?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長殷蛇。 經(jīng)常有香客問我实夹,道長,這世上最難降的妖魔是什么粒梦? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任亮航,我火速辦了婚禮,結(jié)果婚禮上匀们,老公的妹妹穿的比我還像新娘缴淋。我一直安慰自己,他們只是感情好泄朴,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布重抖。 她就那樣靜靜地躺著,像睡著了一般祖灰。 火紅的嫁衣襯著肌膚如雪钟沛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天局扶,我揣著相機與錄音恨统,去河邊找鬼。 笑死三妈,一個胖子當著我的面吹牛畜埋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播畴蒲,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼悠鞍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饿凛?” 一聲冷哼從身側(cè)響起狞玛,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤软驰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后心肪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锭亏,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年硬鞍,在試婚紗的時候發(fā)現(xiàn)自己被綠了慧瘤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡固该,死狀恐怖锅减,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伐坏,我是刑警寧澤怔匣,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站桦沉,受9級特大地震影響每瞒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纯露,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一剿骨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧埠褪,春花似錦浓利、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玉工,卻和暖如春羽资,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遵班。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工屠升, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狭郑。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓腹暖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翰萨。 傳聞我的和親對象是個殘疾皇子脏答,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容