RecyclerView首個(gè)Item滑到頂部漸變

RecyclerView 首個(gè)Item頂部漸變效果

抖音“新鮮”Tab頁(yè)上有個(gè)效果比較有意思模狭,當(dāng)Item滑到頂部的時(shí)候嵌屎,item上的文字信息會(huì)有一個(gè)漸變的隱藏效果,如下圖



這個(gè)效果實(shí)現(xiàn)起來(lái)不是特別難,但是有一些細(xì)節(jié)地方需要我們考慮一下揽涮,下面我就把我實(shí)現(xiàn)的方式分享一下猴娩,先來(lái)一張效果圖


實(shí)現(xiàn)方式

  • 使用RecyclerView.OnScrollListener監(jiān)聽RecyclerView的滾動(dòng)
  • 在onScrolled(RecyclerView recyclerView, int dx, int dy)方法中獲取第一個(gè)Item
  • 獲取第一個(gè)Item的滑動(dòng)出屏幕的百分比
  • 根據(jù)計(jì)算獲取當(dāng)前文字區(qū)域的Alpha值

代碼如下:

RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            //這里我使用的是GridLayoutManager,并且RecyclerView只有兩列
            if(manager instanceof GridLayoutManager){
                GridLayoutManager gridLayoutManager = (GridLayoutManager) manager;
                int firstVisiblePos = gridLayoutManager.findFirstVisibleItemPosition();
                setViewAplha(gridLayoutManager.findViewByPosition(firstVisiblePos));
                setViewAplha(gridLayoutManager.findViewByPosition(firstVisiblePos+1));

                int firstCompletelyVisible = gridLayoutManager.findFirstCompletelyVisibleItemPosition();
                ItemView view1 = (ItemView) gridLayoutManager.findViewByPosition(firstCompletelyVisible);
                ItemView view2 = (ItemView) gridLayoutManager.findViewByPosition(firstCompletelyVisible + 1);
                if(view1 != null){
                    view1.setAlpha(1);
                }
                if(view2 != null){
                    view2.setAlpha(1);
                }
            }
        }
    };


    float beginPercent = 0.2f;
    float endValue = 2;

    private void setViewAplha(View view){
        if (view == null || !(view instanceof ItemView))
            return;
        float p = UIUtils.px2dip(Math.abs((int) view.getY())) * 1.0f / UIUtils.px2dip(view.getHeight()) * 1.0f;
        float curPercent = Float.compare(p - beginPercent, 0.0f) < 0 ? 0.0f : p - beginPercent;
        curPercent = Float.compare(1, curPercent * endValue) < 0 ? 1 : curPercent * endValue;
        view.setAlpha(1 - curPercent);
    }

這里的ItemView是自定義的每一個(gè)內(nèi)容區(qū)域的View冗尤,布局比較簡(jiǎn)單 只有一個(gè)TextView 代碼如下

private static class ItemView extends LinearLayout{
        TextView textView;
        Context mContext;
        public ItemView(Context context) {
            super(context);
            mContext = context;
            init();
        }

        private void init(){
            View root = LayoutInflater.from(mContext).inflate(R.layout.layout_test_item,this);
            textView = (TextView) root.findViewById(R.id.item_text);
        }

        public void setText(String text){
            textView.setText(text);
        }

        public void setAlpha(float alpha){
            textView.setAlpha(alpha);
        }
    }

需要考慮的細(xì)節(jié)問(wèn)題

  • Adapter Holder的復(fù)用
    我們都知道RecyclerView.Adapter會(huì)對(duì)Holder進(jìn)行復(fù)用來(lái)節(jié)約內(nèi)存的開銷,如果假設(shè)一屏有十個(gè)item胀溺,當(dāng)我們滑動(dòng)到第十一個(gè)Item時(shí)會(huì)復(fù)用第一個(gè)Item的Holder裂七,所以在onBindViewHolder方法中我們需要把文字區(qū)域的Alpha值設(shè)置為不透明,adaper的代碼如下:
RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            ItemView itemView = new ItemView(parent.getContext());
            ViewHolder holder = new ViewHolder(itemView);
            return holder;
        }
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            ViewHolder holder1 = (ViewHolder) holder;
            holder1.itemView.setText("我是Item"+position);
            //需要把item設(shè)置不透明仓坞,否則可能會(huì)因?yàn)閺?fù)用導(dǎo)致item剛顯示就是看不見的
            holder1.itemView.setAlpha(1);
        }

        @Override
        public int getItemCount() {
            return 40;
        }
        class ViewHolder extends RecyclerView.ViewHolder{
            ItemView itemView;
            public ViewHolder(View itemView) {
                super(itemView);
                this.itemView = (ItemView) itemView;
            }
        }
    };
  • 快速滑動(dòng)的問(wèn)題
    RecyclerView 滑動(dòng)時(shí)會(huì)有如下三種狀態(tài)
 /**
     * The RecyclerView is not currently scrolling.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_IDLE = 0;

    /**
     * The RecyclerView is currently being dragged by outside input such as user touch input.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_DRAGGING = 1;

    /**
     * The RecyclerView is currently animating to a final position while not under
     * outside control.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_SETTLING = 2;

當(dāng)我們快速滑動(dòng)時(shí)可能會(huì)由于滑動(dòng)速度過(guò)快背零,導(dǎo)致在下次回調(diào)時(shí)可能已經(jīng)是下一個(gè)item元素,例如无埃,在當(dāng)前onScrolled方法回調(diào)時(shí)徙瓶,findFirstVisibleItemPosition可能是item4,然后我們計(jì)算出文字區(qū)域的Alpha是0.6嫉称,因?yàn)榛瑒?dòng)過(guò)快侦镇,下次回調(diào)onScrolled方法時(shí),findFirstVisibleItemPosition可能就變成了item6织阅,Alpha值是0.8壳繁,因?yàn)槲覀內(nèi)鄙倭薸tem4的完整狀態(tài),就導(dǎo)致了當(dāng)快速滑動(dòng)停止時(shí),可能item6是正確的透明度闹炉,但是我們希望item4是完全不透明的蒿赢,但是實(shí)際卻是0.6
為了解決這個(gè)問(wèn)題,我嘗試了幾種方法渣触,像監(jiān)聽滑動(dòng)狀態(tài)啊羡棵、判斷onScrolled中的dy參數(shù)啊,后來(lái)我發(fā)現(xiàn)我有點(diǎn)給想的麻煩了嗅钻,其實(shí)不管滑動(dòng)速度多快皂冰,每個(gè)item在滑動(dòng)到第一個(gè)完全可展示的位置時(shí),是一定會(huì)被我們知道的养篓,比如雖然我們不能拿到item4從完全透明到完全不透明的所有狀態(tài)灼擂,但是當(dāng)item6是第一個(gè)item時(shí),item4一定是第一可以完全展示的item(我的RecyclerView有兩列)觉至,所以我們就可以通過(guò)這個(gè)點(diǎn)來(lái)下手剔应,通過(guò)LayoutManager的findFirstCompletelyVisibleItemPosition來(lái)找到第一個(gè)完全可見的Item位置,具體體現(xiàn)就是剛才RecyclerView.OnScrollListener 里面的代碼

           int firstCompletelyVisible = gridLayoutManager.findFirstCompletelyVisibleItemPosition();
           ItemView view1 = (ItemView) gridLayoutManager.findViewByPosition(firstCompletelyVisible);
           ItemView view2 = (ItemView) gridLayoutManager.findViewByPosition(firstCompletelyVisible + 1);
            if(view1 != null){
                view1.setAlpha(1);
            }
            if(view2 != null){
                view2.setAlpha(1);
            }
  • Alpha值的計(jì)算
    這里還有一個(gè)要考慮的問(wèn)題语御,就是Alpha的取值問(wèn)題峻贮,仔細(xì)看抖音中的效果,并不是item剛開始滑動(dòng)就開始改變透明度应闯,而是大概Item滑出屏幕1/5左右開始纤控,這里就需要我們做一點(diǎn)點(diǎn)小小的計(jì)算,當(dāng)然我的計(jì)算方法可能有點(diǎn)low碉纺,計(jì)算方法就是剛才的setViewAplha里面所做的船万,在這在粘貼一下
    float beginPercent = 0.2f;
    float endValue = 2;

    private void setViewAplha(View view){
        if (view == null || !(view instanceof ItemView))
            return;
        float p = UIUtils.px2dip(Math.abs((int) view.getY())) * 1.0f / UIUtils.px2dip(view.getHeight()) * 1.0f;
        float curPercent = Float.compare(p - beginPercent, 0.0f) < 0 ? 0.0f : p - beginPercent;
        curPercent = Float.compare(1, curPercent * endValue) < 0 ? 1 : curPercent * endValue;
        view.setAlpha(1 - curPercent);
    }

這個(gè)beginPercent變量表示我想要在Item滑出屏幕多少時(shí),才開始改變Alpha值骨田,這里我定義成了0.2也就是1/5耿导,然后在第一次計(jì)算curPercent時(shí)

  float p = UIUtils.px2dip(Math.abs((int) view.getY())) * 1.0f / UIUtils.px2dip(view.getHeight()) * 1.0f;
  float curPercent = Float.compare(p - beginPercent, 0.0f) < 0 ? 0.0f : p - beginPercent;

可見如果當(dāng)p<0.2時(shí),我的curPercent會(huì)等于0态贤,后續(xù)在做什么計(jì)算都不會(huì)改變文字區(qū)域的Alpha值
現(xiàn)在已經(jīng)可以設(shè)置從item滑動(dòng)到多少才開始出現(xiàn)漸變的效果了舱呻,那么下一步我們就應(yīng)該定義當(dāng)item滑動(dòng)到什么位置時(shí)文字區(qū)域完全透明也就是Alpha值等于0,這時(shí)候endValue的作用就來(lái)了悠汽,我們可以通過(guò)給curPercent乘一個(gè)值來(lái)讓它達(dá)到指定位置時(shí)可以變成1箱吕,然后1-curPercent=0

curPercent = Float.compare(1, curPercent * endValue) < 0 ? 1 : curPercent * endValue;

這里我的endValue=2,可以在item滑出屏幕70%時(shí)柿冲,文字區(qū)域完全透明,我的計(jì)算方法如下表格

curPercent 與0.2的差值(滑動(dòng)從1/5開始計(jì)算)
0.0 -
0.1 -
0.2 -
0.3 0.1
0.4 0.2
0.5 0.3
0.6 0.4
0.7 0.5
0.8 0.6
0.9 0.7

所以如果我想讓Item滑動(dòng)到70%時(shí)完全透明假抄,我只要設(shè)置endValue是2既可怎栽,因?yàn)榇藭r(shí)curPercent是0.7-0.2=0.5 然后乘2剛好等于1,之前一直都會(huì)小于1婚瓜,這樣的話刑棵,我們可以隨意設(shè)定滑動(dòng)漸變從哪開始到哪結(jié)束巴刻,比如我現(xiàn)在希望item滑出屏幕30%開始 同樣在item滑出屏幕70%完全透明那么根據(jù)下面這個(gè)表格

curPercent 與0.3的差值(滑動(dòng)從30%開始計(jì)算)
0.0 -
0.1 -
0.2 -
0.3 -
0.4 0.1
0.5 0.2
0.6 0.3
0.7 0.4
0.8 0.5
0.9 0.6

我們可以計(jì)算出 endValue=3( (0.7-0.3)* endValue =1.2 )即可保證在滑動(dòng)到70%時(shí) 完全的透明

ok 到這我們就可以完美表現(xiàn)出這種漸變的效果了蛉签,這是我第一次寫簡(jiǎn)書,寫的還有點(diǎn)小激動(dòng)呢碍舍。歡迎所有讀過(guò)的人 批評(píng)指正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市片橡,隨后出現(xiàn)的幾起案子妈经,更是在濱河造成了極大的恐慌捧书,老刑警劉巖吹泡,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異经瓷,居然都是意外死亡爆哑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門舆吮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)揭朝,“玉大人,你說(shuō)我怎么就攤上這事色冀√陡ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵锋恬,是天一觀的道長(zhǎng)敌卓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)伶氢,這世上最難降的妖魔是什么趟径? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮癣防,結(jié)果婚禮上蜗巧,老公的妹妹穿的比我還像新娘。我一直安慰自己蕾盯,他們只是感情好幕屹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般望拖。 火紅的嫁衣襯著肌膚如雪渺尘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天说敏,我揣著相機(jī)與錄音鸥跟,去河邊找鬼。 笑死盔沫,一個(gè)胖子當(dāng)著我的面吹牛医咨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播架诞,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拟淮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谴忧?” 一聲冷哼從身側(cè)響起很泊,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沾谓,沒(méi)想到半個(gè)月后委造,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搏屑,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年亮垫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟骨。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖继蜡,靈堂內(nèi)的尸體忽然破棺而出逛腿,到底是詐尸還是另有隱情,我是刑警寧澤单默,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站引颈,受9級(jí)特大地震影響耕皮,放射性物質(zhì)發(fā)生泄漏蝙场。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一罚拟、第九天 我趴在偏房一處隱蔽的房頂上張望趴泌。 院中可真熱鬧拉庶,春花似錦嗜憔、人聲如沸吉捶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慷蠕。三九已至,卻和暖如春流炕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背每辟。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妹蔽,地道東北人挠将。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像舔稀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子镶蹋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Tangram是阿里出品、用于快速實(shí)現(xiàn)組合布局的框架模型淆两,在手機(jī)天貓Android&iOS版 內(nèi)廣泛使用 該框架提...
    wintersweett閱讀 3,301評(píng)論 0 1
  • 生活本來(lái)就不容易,而我們的不努力只會(huì)讓生活變得更加無(wú)賴 前幾天依據(jù)《Android群英傳》的學(xué)習(xí)寫了一篇筆記是關(guān)于...
    AmatorLee閱讀 3,745評(píng)論 7 23
  • 寫在前面:參考YoKey秋冰,感謝。附上他的鏈接:http://www.reibang.com/p/d30fd8da4...
    喜歡丶下雨天閱讀 3,233評(píng)論 0 9
  • 這篇文章分三個(gè)部分剑勾,簡(jiǎn)單跟大家講一下 RecyclerView 的常用方法與奇葩用法;工作原理與ListView比...
    LucasAdam閱讀 4,391評(píng)論 0 27
  • “本文參加#感悟三下鄉(xiāng)暂刘,青春筑夢(mèng)行#活動(dòng),本人承諾谣拣,文章內(nèi)容為原創(chuàng),且未在其他平臺(tái)發(fā)表過(guò)森缠。” 我贵涵,叫李新蓉 是來(lái)自...
    阿蓉_9a4a閱讀 790評(píng)論 0 6