我把Android 10手勢導航的側(cè)滑返回效果優(yōu)化了一波

穩(wěn)住,這里是ROM開發(fā)臂港!

Android 10 中引入了手勢導航功能森枪,向IOS看齊了一步,但是默認的這個側(cè)滑返回效果實在是差強人意审孽,效果如下:

0.gif

國內(nèi)各大廠商也都對此做了優(yōu)化县袱,那么我們也來優(yōu)化一下吧,效果如下:

final.gif

1瓷胧,找到地方

說的夠通俗易懂了吧显拳,要改它,就要先找到它在哪里實現(xiàn)的搓萧。說實話這個還真不好找杂数,網(wǎng)上有說:Android 10中手勢導航為了xxx放到了Launcher中,然后通過AIDL和System UI交互瘸洛。揍移。。在Launcher中找了半天也沒找到反肋。

如果不是在Launcher中那就肯定在System UI中那伐,一開始沒有頭緒,只能一(瞎)點(幾)點(把)找石蔗。后來考慮到這個是拖拽效果罕邀,那就肯定跟觸摸事件相關(guān),本來想著在System UI的所有MotionEvent.ACTION_MOVE處打斷點养距,沒想到在一個個添加斷點的時候發(fā)現(xiàn)了EdgeBackGesturehandler.java這個類诉探,在其中打斷了,調(diào)試果然命中了棍厌。

機智

不過這個類是做控制用的肾胯,真正的繪制實現(xiàn)是在src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel里面竖席。

NavigationBarEdgePanel是一個自定義view,如下:

public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin {
    ...
}

既然找到了地方敬肚,那就實現(xiàn)一下上下滑動吧毕荐。

2,實現(xiàn)上下移動

默認的那個箭頭顯示出來以后不會跟著手勢上下移動艳馒,要實現(xiàn)上下移動就要先知道這個視圖是怎么添加上去的憎亚。

首先看一下EdgeBackGesturehandler,在其中初始化了NavigationBarEdgePanel并且設(shè)置了WindowManager.LayoutParams

// 第一步:創(chuàng)建Panel
// Add a nav bar panel window
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));

// 第二步:設(shè)置LayoutParams
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
    ...
    mEdgeBackPlugin = edgeBackPlugin;
    // 設(shè)置返回回調(diào)
    mEdgeBackPlugin.setBackCallback(mBackCallback);
    // 設(shè)置LayoutParams
    mEdgeBackPlugin.setLayoutParams(createLayoutParams());
    updateDisplaySize();
}

這個類中并沒有看到視圖如何添加到屏幕上的鹰溜。

那么再來看看NavigationBarEdgePanel虽填。

// 1,構(gòu)造方法中獲取WindowManager
public NavigationBarEdgePanel(Context context) {
    mWindowManager = context.getSystemService(WindowManager.class);
    ...
}

@Override
public void setLayoutParams(WindowManager.LayoutParams layoutParams) {
    mLayoutParams = layoutParams;
    // 2曹动,把當前view添加到WindowManager中
    mWindowManager.addView(this, mLayoutParams);
}

在構(gòu)造方法中獲取了WindowManager斋日,然后在setLayoutParams() 中將當前view添加到WindowManager中。大致了解了它是怎么顯示出來的之后墓陈,就可以修改它的位置了恶守。

既然是想讓他跟著手指上下滑動,那可定跟觸摸事件相關(guān)贡必,但是在NavigationBarEdgePanel的觸摸滑動事件中沒有對視圖位置的更新兔港,因此只需要在滑動時更新位置就好了:

private void handleMoveEvent(MotionEvent event) {
    float y = event.getY();
    ...
    // 當前位置-上次的位置就是移動的偏移量,將偏移量累加到mLayoutParams當前的y上即可
    mLayoutParams.y += y - mLastY;
    // 更新位置
    mWindowManager.updateViewLayout(this, mLayoutParams);
}

計算出每次移動的偏移量仔拟,然后將偏移量累加到mLayoutParams當前的y上即可衫樊,最后通過WindowManager更新一下視圖位置即可。

這里就不貼圖了利花,看最終效果圖就可以了科侈。

3,實現(xiàn)拖拽時凸起效果

由于修改System UI的代碼時沒有代碼補全提示炒事,并且難以調(diào)試臀栈,那就先搞個demo實現(xiàn)一下拖拽凸起效果,然后再集成到System UI中挠乳。

這個是自定義view相關(guān)的姿勢权薯,假設(shè)你對自定義view有一定了解。

2.1 自定義view

首先新建一個demo睡扬,創(chuàng)建一個自定義的View盟蚣。重寫onMeasure()onDraw() 吧啦吧啦卖怜。刁俭。。

2.2 事件處理

主要邏輯為:按下屏幕時判斷是否在邊界韧涨,在的話消費掉該事件牍戚;然后在滑動時記錄當前的位置,用于繪制時的處理虑粥;最后在離開屏幕時釋放掉相關(guān)的資源如孝。

2.3 繪制

要繪制這么一個區(qū)域,需要用到貝塞爾曲線娩贷,這里可以分為兩個三階貝塞爾曲線第晰。如下,P0和P6是屏幕邊界上的兩個點彬祖,P1和P2分別是上半部分的控制點茁瘦,P4和P5是下半部分的控制點。

wave.jpg
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 每次繪制重置path
    mPath.reset();
    
    int factor = isLeft ? 1 : -1;

    // 凸起上部Y
    int topY = currentY - 150;
    // 凸起下部Y
    int bottomY = currentY + 150;
    // 凸起底部X
    int footX = isLeft ? 0 : mWidth;
    // 凸起頂部X
    int peekX = footX + factor * mOffset;
    // 控制點p1
    p1.x = footX;
    p1.y = topY + 75;
    // 控制點p2
    p2.x = peekX;
    p2.y = topY + 100;
    // 控制點p4
    p4.x = peekX;
    p4.y = bottomY - 100;
    // 控制點p5
    p5.x = footX;
    p5.y = bottomY - 75;
    
    mPath.moveTo(footX, topY);
    // 上半部分貝塞爾曲線
    mPath.cubicTo(p1.x, p1.y, p2.x, p2.y, peekX, currentY);
    // 下半部分貝塞爾曲線
    mPath.cubicTo(p4.x, p4.y, p5.x, p5.y, footX, bottomY);
    mPath.close();
    // 繪制path
    canvas.drawPath(mPath, mPaint);
}

經(jīng)過一波簡單的操作储笑,效果如下:

2.gif

效果還行吧甜熔,那么怎么把它弄到System UI中呢?

4突倍,集成拖拽時凸起效果

回到NavigationBarEdgePanel中腔稀,在onDraw()中只需將demo中的代碼稍微修改一下即可:

@Override
protected void onDraw(Canvas canvas) {
    // 箭頭所在位置
    float pointerPosition = mCurrentTranslation - mArrowThickness / 2.0f;

    ...

    // draw wave
    mPath.reset();

    int factor = mIsLeftPanel ? 1 : -1;
    int currentY = getHeight() / 2;
    // 凸起上部Y
    int topY = 0;
    // 凸起下部Y
    int bottomY = getHeight();
    // 凸起底部X
    int footX = mIsLeftPanel ? 0 : getWidth();
    // 凸起頂部X
    int peekX = (int) (footX + factor * (mIsLeftPanel ? pointerPosition : pointerPosition - getStaticArrowWidth()) / 2);

    p1.x = footX;
    p1.y = topY + 75;

    p2.x = peekX;
    p2.y = topY + 100;

    p4.x = peekX;
    p4.y = bottomY - 100;

    p5.x = footX;
    p5.y = bottomY - 75;

    mPath.moveTo(footX, topY);
    mPath.cubicTo(p1.x, p1.y, p2.x, p2.y, peekX, currentY);
    mPath.cubicTo(p4.x, p4.y, p5.x, p5.y, footX, bottomY);
    mPath.close();

    canvas.drawPath(mPath, mWavePaint);
}

效果如下:

1.gif

5,優(yōu)化箭頭

默認的箭頭太丑了羽历,接下來把箭頭優(yōu)化一下焊虏。

首先找到默認繪制箭頭的地方,給他咔嚓掉秕磷,然后只需要繪制一個圓形并在上面繪制一個箭頭即可诵闭。

@Override
protected void onDraw(Canvas canvas) {
    
    // (circleX, centerY)是圓形的中心店
    // 繪制圓形
    canvas.drawCircle(circleX, centerY, dp(10), mWavePaint);

    // 繪制返回 
    mArrowPath.reset();
    mArrowPath.moveTo(circleX + dp(2.5f), centerY - dp(5.5f));
    mArrowPath.lineTo(circleX - dp(3f), centerY);
    mArrowPath.lineTo(circleX + dp(2.5f), centerY + dp(5.5f));
    canvas.drawPath(mArrowPath, mArrowPaint);
}

這個沒啥說的,就是繪制一個circle澎嚣,然后通過path繪制一個箭頭即可疏尿,效果如下:

final.gif

最后:本文簡單記錄了一下實現(xiàn)過程,大佬勿噴币叹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末润歉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颈抚,更是在濱河造成了極大的恐慌踩衩,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贩汉,死亡現(xiàn)場離奇詭異驱富,居然都是意外死亡,警方通過查閱死者的電腦和手機匹舞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門褐鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赐稽,你說我怎么就攤上這事叫榕』虢模” “怎么了?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵晰绎,是天一觀的道長寓落。 經(jīng)常有香客問我,道長荞下,這世上最難降的妖魔是什么伶选? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮尖昏,結(jié)果婚禮上仰税,老公的妹妹穿的比我還像新娘。我一直安慰自己抽诉,他們只是感情好陨簇,可當我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掸鹅,像睡著了一般塞帐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巍沙,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天葵姥,我揣著相機與錄音,去河邊找鬼句携。 笑死榔幸,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的矮嫉。 我是一名探鬼主播削咆,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蠢笋!你這毒婦竟也來了拨齐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤昨寞,失蹤者是張志新(化名)和其女友劉穎瞻惋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體援岩,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡歼狼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了享怀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羽峰。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梅屉,到底是詐尸還是另有隱情值纱,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布履植,位于F島的核電站计雌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玫霎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一妈橄、第九天 我趴在偏房一處隱蔽的房頂上張望庶近。 院中可真熱鬧,春花似錦眷蚓、人聲如沸鼻种。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叉钥。三九已至,卻和暖如春篙贸,著一層夾襖步出監(jiān)牢的瞬間投队,已是汗流浹背疚沐。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工昔穴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吆录,地道東北人定躏。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓神得,卻偏偏與公主長得像悼吱,于是被迫代替她去往敵國和親乐导。 傳聞我的和親對象是個殘疾皇子肯骇,可洞房花燭夜當晚...
    茶點故事閱讀 43,595評論 2 350