Android - Toolbar事件穿透和背景漸變

摘要:

最近公司的App做了一次改版凳干,對(duì)UI頁(yè)面做了一些用戶體驗(yàn)上的優(yōu)化枕面。

(本文是對(duì)本次工作的踩坑總結(jié))

頁(yè)面效果:

本次App改版嗅绰,筆者這牽扯到兩個(gè)功能點(diǎn)的實(shí)現(xiàn):

  1. 點(diǎn)擊“眼睛圖標(biāo)”娘侍,實(shí)現(xiàn)顯示/隱藏各類資金 (老版有此功能大溜,本次做些調(diào)整)

  2. 實(shí)現(xiàn)導(dǎo)航欄背景圖漸變

app.gif

實(shí)現(xiàn)策略:

功能一:點(diǎn)擊“眼睛圖標(biāo)”技矮,實(shí)現(xiàn)顯示/隱藏各類資金

第1個(gè)功能點(diǎn)很簡(jiǎn)單抖誉,其實(shí)就是監(jiān)聽“眼睛圖標(biāo)”的點(diǎn)擊事件,將需要顯示的“數(shù)字”顯示成固定文本“ **** ”并將“開眼圖標(biāo)”設(shè)置成“閉眼圖標(biāo)”衰倦。點(diǎn)擊“閉眼圖標(biāo)”時(shí)袒炉,對(duì)之前的操作進(jìn)行還原。

理論上是這樣沒(méi)有錯(cuò)樊零,但實(shí)際操作起來(lái)就會(huì)發(fā)現(xiàn)有坑了/(ㄒoㄒ)/~

當(dāng)筆者在點(diǎn)擊“眼睛圖標(biāo)”時(shí)我磁,發(fā)現(xiàn)Toolbar將點(diǎn)擊事件攔截了孽文,事件無(wú)法傳遞到下層布局......

那么怎樣才能穿透Toolbar將點(diǎn)擊事件分發(fā)到下面的布局呢?

Toolbar是一個(gè)懸浮在最上層的夺艰、獨(dú)立的ViewGroup芋哭,里面沒(méi)有包含下層的內(nèi)容布局。而且Toolbar源碼中onTouchEvent方法的返回值為true郁副,意味著事件最多傳遞到這就會(huì)被消費(fèi)掉减牺。

Toolbar.png

難道要自定義Toolbar,重寫onTouchEvent方法存谎?這是筆者最初的想法拔疚,但是后來(lái)想了想,這種修改源碼的方式有點(diǎn)作死(因?yàn)楣P者沒(méi)有詳細(xì)看過(guò)Toolbar的源碼既荚,而且就算看過(guò)Toolbar的源碼稚失,源碼后期發(fā)生變更時(shí)還要維護(hù))。

筆者的XML布局層次(簡(jiǎn)化版):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout >

    <!-- 內(nèi)容部分 -->
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout">
        
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view" />      
    </android.support.v4.widget.SwipeRefreshLayout>

    <!-- 標(biāo)題欄 -->
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
    </android.support.v7.widget.Toolbar>
</RelativeLayout>

我們可以看到“標(biāo)題欄”和“內(nèi)容部分”在布局上是“平行關(guān)系”固以,但實(shí)際在視圖上“標(biāo)題欄”是在“內(nèi)容部分”的上方墩虹。

尋找解決辦法:

筆者思考了很長(zhǎng)時(shí)間,想出了一個(gè)相對(duì)靠譜的解決辦法:Toolbar執(zhí)行到onTouchEvent之后憨琳,事件就被消費(fèi)了诫钓。我們可以在這之前搞點(diǎn)事情,比如改變事件流的傳遞方向篙螟,將事件流向下層布局分發(fā)菌湃。

實(shí)現(xiàn)事件穿透的代碼(僅包含觸摸監(jiān)聽器的實(shí)現(xiàn)):

// 穿透Toolbar的點(diǎn)擊事件,向下層分發(fā)處理
mToolbarOnTouchListener = new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return findViewById(R.id.swipe_refresh_layout).dispatchTouchEvent(event);
    }
};

略懂事件傳遞機(jī)制的Android程序員應(yīng)該都知道,View觸摸監(jiān)聽器的onTouch方法會(huì)在onTouchEvent方法執(zhí)行完成之前執(zhí)行遍略,那么我們可以在onTouch方法里再進(jìn)行1次事件分發(fā)惧所,調(diào)用“內(nèi)容部分”的最外層布局SwipeRefreshLayout的分發(fā)方法dispatchTouchEvent(MotionEvent event),并將正在Toolbar中進(jìn)行傳遞的event事件作為參數(shù)傳遞進(jìn)去绪杏,讓SwipeRefreshLayout繼續(xù)處理該事件下愈,從而實(shí)現(xiàn)事件穿透

解決辦法終于找到了蕾久,那么直接上來(lái)就給Toolbar設(shè)置完這個(gè)觸摸監(jiān)聽器就萬(wàn)事大吉了嗎势似?

我們還做了一個(gè)背景圖片漸變的功能,Toolbar背景不可見或半透明時(shí)響應(yīng)下層布局的點(diǎn)擊事件很正常僧著,很容易理解履因。但是背景圖片如果完全可見,點(diǎn)擊Toolbar還響應(yīng)下層布局的點(diǎn)擊事件盹愚,是不是用戶體驗(yàn)上有點(diǎn)別扭呢栅迄?(全都是細(xì)節(jié))

最終的解決方案 (動(dòng)態(tài)給Toolbar設(shè)置監(jiān)聽器):

  • 當(dāng)背景圖片完全可見之前,給Toolbar設(shè)置觸摸監(jiān)聽器mToolbarOnTouchListener皆怕。

    mToolbar.setOnTouchListener(mToolbarOnTouchListener);
    
  • 當(dāng)背景圖片完全可見之后毅舆,將Toolbar的觸摸監(jiān)聽器設(shè)置為null西篓。

    mToolbar.setOnTouchListener(null);
    

至此,第一個(gè)問(wèn)題就描述完了憋活。

功能二:實(shí)現(xiàn)導(dǎo)航欄背景圖漸變

之前筆者做過(guò)Toolbar背景顏色漸變的效果污淋,但筆者的App基本上每個(gè)頁(yè)面的Toolbar背景用的都是圖片,不是顏色余掖。仔細(xì)想了想寸爆,圖片的漸變也只能用透明度來(lái)搞了。

在網(wǎng)上百度&谷歌了一下盐欺,終于找到了設(shè)置圖片透明度的方法(大致3步)

// 獲取Drawable對(duì)象
Drawable mDrawable = ContextCompat.getDrawable(mActivity, R.drawable.lcs_actionbar_bg2);
// 設(shè)置Drawable的透明度
mDrawable.setAlpha(255);
// 給Toolbar設(shè)置背景圖
mToolbar.setBackgroundDrawable(mDrawable);

有了解決方案終于可以開工了:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

    // 獲取背景圖片
    Drawable mDrawable = ContextCompat.getDrawable(mActivity, R.drawable.lcs_actionbar_bg2);

    @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);
        
        // 省略了與漸變背景圖無(wú)關(guān)的代碼
        ......
        
        int toolBarHeight = mToolbar.getMeasuredHeight();
        if ((recyclerView.computeVerticalScrollOffset()) >= (toolBarHeight * 2.5)) { // >=Toolbar高度的2.5倍時(shí)全顯背景圖
            mDrawable.setAlpha(255);
            mToolbar.setBackgroundDrawable(mDrawable);
            mToolbar.setOnTouchListener(null);
        } else if((recyclerView.computeVerticalScrollOffset()) >= toolBarHeight){ // >=Toolbar高度&&<Toolbar高度的2.5倍時(shí)開始漸變背景圖
            mDrawable.setAlpha((int) (255 * ((recyclerView.computeVerticalScrollOffset() - toolBarHeight)/(toolBarHeight * 1.5F))));
            mToolbar.setBackgroundDrawable(mDrawable);
            mToolbar.setOnTouchListener(mToolbarOnTouchListener);
        } else { // 小于Toolbar高度時(shí)不設(shè)置背景圖
            mToolbar.setBackgroundDrawable(null);
            mToolbar.setOnTouchListener(mToolbarOnTouchListener);
        }
    }
});

RecyclerView.computeVerticalScrollOffset() 可以返回給我們r(jià)ecyclerView豎直滾動(dòng)的距離赁豆。

筆者給RecyclerView添加了一個(gè)滾動(dòng)監(jiān)聽器,根據(jù)RecyclerView豎直滾動(dòng)的距離動(dòng)態(tài)決定背景圖片的透明度冗美,具體細(xì)節(jié)如下:

  • 當(dāng)RecyclerView豎直滾動(dòng)距離<Toolbar的高度時(shí)魔种,不設(shè)置背景圖(Toolbar背景透明),并設(shè)置上觸摸監(jiān)聽器(穿透Toolbar點(diǎn)擊事件)粉洼。

  • 當(dāng)RecyclerView豎直滾動(dòng)距離>=Toolbar的高度且<2.5倍的Toolbar高度時(shí)节预,漸變背景圖,并設(shè)置上觸摸監(jiān)聽器(穿透Toolbar點(diǎn)擊事件)属韧。

  • RecyclerView豎直滾動(dòng)距離>=2.5倍的Toolbar高度時(shí)安拟,全顯背景圖,觸摸監(jiān)聽器設(shè)置為null宵喂。

接著運(yùn)行了一下糠赦,發(fā)現(xiàn)已經(jīng)實(shí)現(xiàn)了文章開始時(shí)的動(dòng)態(tài)圖效果。

之后锅棕,放心的打包完App提交給測(cè)試MM測(cè)試了拙泽。但好景不長(zhǎng),測(cè)試MM那測(cè)試機(jī)類型很多裸燎,在很多華為手機(jī)上測(cè)出了一個(gè)Bug顾瞻,如下圖所示:

app_bug.gif

可以看到,在華為手機(jī)上德绿,筆者的APP中所有用到該背景圖的地方都會(huì)受到透明度影響荷荤。

后來(lái)從網(wǎng)上查閱了一些資料,才知道原來(lái)華為手機(jī)是做了一些優(yōu)化處理的脆炎。同一張圖片(比如R.drawable.lcs_actionbar_bg)在被轉(zhuǎn)化成Drawable對(duì)象并在內(nèi)存中使用時(shí)會(huì)被“共享”梅猿,意味著筆者App中所有對(duì)這個(gè)Drawable(R.drawable.lcs_actionbar_bg)的操作全都是在對(duì)同一個(gè)對(duì)象進(jìn)行操作氓辣。

華為手機(jī)的這種處理方式減少多余對(duì)象的生成秒裕,減少了內(nèi)存暫用,減少了GC钞啸,仔細(xì)想想還是挺贊的几蜻。(好像扯遠(yuǎn)了喇潘,該回歸正題想想怎么處理了o(╯□╰)o....)

一開始想了想,覺(jué)得可以這樣處理:在離開這個(gè)頁(yè)面時(shí)梭稚,記錄一下透明度颖低,把Drawable背景圖設(shè)置為完全可見的;回到這個(gè)頁(yè)面時(shí)弧烤,根據(jù)記錄值還原Drawable的透明度忱屑。

理想是美好的,現(xiàn)實(shí)是殘酷的暇昂。這個(gè)頁(yè)面的管理器是一個(gè)Fragment莺戒,筆者在onStop方法里做了上述這種處理,但還是會(huì)偶發(fā)出現(xiàn)這種透明度的Bug急波,看來(lái)筆者還有很多細(xì)節(jié)沒(méi)有考慮到(怎么感覺(jué)這細(xì)節(jié)越陷越深了/(ㄒoㄒ)/~~)从铲。

最終的解決辦法:

筆者無(wú)奈之下放棄了之前的處理方式,想了一個(gè)投機(jī)取巧的辦法:

把資源文件 lcs_actionbar_bg.png 又復(fù)制了一份,命名為lcs_actionbar_bg2.png

lcs_actionbar_bg2.png

lcs_actionbar_bg2.png 這張圖片獨(dú)立為這個(gè)導(dǎo)航欄漸變的頁(yè)面服務(wù)澄暮,不會(huì)影響到其他界面名段。

最后,華為手機(jī)導(dǎo)航欄透明度的Bug終于解決了泣懊。

題外話

有朋友問(wèn):搞個(gè)標(biāo)題欄特效為什么不用 AppBarLayout + CollapsingToolbarLayou + Toolbar 做個(gè)折疊效果伸辟?這個(gè)應(yīng)該很簡(jiǎn)單吧?

答:產(chǎn)品說(shuō)我們的App很多頁(yè)面都有這種效果馍刮,還是換種方式實(shí)現(xiàn)吧...

最后發(fā)表一下感慨:

mind.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末自娩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渠退,更是在濱河造成了極大的恐慌忙迁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碎乃,死亡現(xiàn)場(chǎng)離奇詭異姊扔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)梅誓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門恰梢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人梗掰,你說(shuō)我怎么就攤上這事嵌言。” “怎么了及穗?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵摧茴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我埂陆,道長(zhǎng)苛白,這世上最難降的妖魔是什么娃豹? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮购裙,結(jié)果婚禮上懂版,老公的妹妹穿的比我還像新娘。我一直安慰自己躏率,他們只是感情好躯畴,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薇芝,像睡著了一般私股。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恩掷,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天倡鲸,我揣著相機(jī)與錄音,去河邊找鬼黄娘。 笑死峭状,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逼争。 我是一名探鬼主播优床,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼誓焦!你這毒婦竟也來(lái)了胆敞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤杂伟,失蹤者是張志新(化名)和其女友劉穎移层,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赫粥,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡观话,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了越平。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片频蛔。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秦叛,靈堂內(nèi)的尸體忽然破棺而出晦溪,到底是詐尸還是另有隱情,我是刑警寧澤挣跋,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布三圆,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫌术。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一牌借、第九天 我趴在偏房一處隱蔽的房頂上張望度气。 院中可真熱鬧,春花似錦膨报、人聲如沸磷籍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)院领。三九已至,卻和暖如春够吩,著一層夾襖步出監(jiān)牢的瞬間比然,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工周循, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留强法,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓湾笛,卻偏偏與公主長(zhǎng)得像饮怯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嚎研,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評(píng)論 25 707
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,708評(píng)論 22 664
  • 回顧 1.學(xué)習(xí)使用eclipse開發(fā)工具 2.復(fù)習(xí)超簡(jiǎn)單代碼實(shí)例兩個(gè) 3.回顧Java程序的基本組成蓖墅。 學(xué)習(xí)小結(jié) ...
    礫桫_Yvan閱讀 234評(píng)論 0 1
  • 我入花城夏季 細(xì)品多彩多姿 大街紅荔擺美態(tài) 小巷白蘭送香氣 漫湖碧荷葉吻風(fēng) 滿城金鳳花映日 相比春的溫柔秋的涼爽 ...
    珠江潮平閱讀 191評(píng)論 6 8
  • 我真是找了一份感覺(jué)難爆了的工作……最主要的是,有一個(gè)能把這些事情做的手到擒來(lái)游刃有余的神奇BOSS临扮。 今天早上她把...
    嫏嬛素素閱讀 186評(píng)論 2 0