Android之實現妙趣橫生的粘連佈局
1概述
在手機QQ中,有一個功能叫“一鍵下班”,無論介面有多少資訊,只要你不想看,就可以手指一滑,將他們全部消滅。據說這個功能專為紅點恐懼症、資訊閱讀強迫症以及處女座暖心打造。這個功能已經上線許久,除了設計本身比較貼心外,其呈現效果也十分驚豔:
這個功能深受廣大使用者喜愛,那麼這樣一個場景到底是怎樣的實現的呢?面對各位開發哥哥的疑問,小編決定玩個大的,既然要寫,不如就寫一個拓展性強大一點的、不僅僅只適用於“一鍵下班”場景的吧,乾脆叫它粘連佈局 —— AdherentLayout。
2AdherentLayout
AdherentLayout是一個適用於粘連場景的的開源元件,它有以下特性:
1、除了可實現類似手Q“一鍵下班”場景,還可以實現其他你能想到的其他場景。
2、支援設定粘連長度、粘連頭部大小、粘連顏色、是否可以扯斷的設定。
3、支援使用者自定義粘連尾部的檢視定製。
4、支援扯斷時候的監聽事件。
3使用方法
<AdherentLayout>
<!-- 在這裡可以新增使用者的檢視(可選) -->
< />
</AdherentLayout>
4介面說明
setColor(int color)
設定粘連顏色
setDismissedEnable(boolean isDismissed)
設定是否可以扯斷
setMaxAdherentLength(int maxAdherentLength)
設定粘連的最大長度
setMinHeaderCircleRadius(int minHeaderCircleRadius)
設定粘連頭部圓的最小半徑
setOnAdherentListener(OnAdherentListener onAdherentListener)
設定粘連事件的監聽器
5核心技術
要實現粘連效果,最重要的是掌握貝塞爾曲線的繪製。
6貝塞爾曲線
通俗的講,貝塞爾曲線就是用來精確畫出曲線的,通過若干個控制點來控制曲線的形狀與繪製。因為Android本身自帶支援二階、三階的貝塞爾曲線繪製的API,所以下面就只引出二階、三階的動態圖好了,具體的知識點可移步貝塞爾曲線初探。
二階:
三階:
從上圖可知,
二階是隻有一個控制點,對應quadTo(float x1, float y1, float x2, float y2),其中(x1,y1)是控制點,(x2,y2)是結束點。
三階有兩個控制點,對應cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) ,其中(x1,y1)是第一控制點,(x2,y2)是第二控制點,(x3,y3)是結束點。
7具體實現
借用ISUX的一張圖,該粘連佈局的具體流程分以下兩種情況:
1、未超出粘連範圍:邊拖拽邊繪製粘連頭部圓、粘連尾部圓和粘連體。其中頭部圓和尾部圓都是用drawCircle進行繪製,粘連體通過p1、p2、p3、p4、控制點採用quadTo繪製兩條二階貝塞爾曲線並分別連線p1p3、p2p4閉合起來,取兩圓心距離的中點為控制點,通過拖拽過程中兩圓心的距離之比來控制頭部圓的放大縮小即可。鬆開手勢,開啟回彈動畫。
2、超出粘連範圍:只繪製粘連尾部圓即可。鬆開手勢,繪製結束。
其中,繪製粘連體的核心程式碼如下:
private void drawBezier(Canvas canvas) {
/* 求三角函式 */
float atan = (float) Math.atan((mFooterCircle.y - mHeaderCircle.y) / (mFooterCircle.x - mHeaderCircle.x));
float sin = (float) Math.sin(atan);
float cos = (float) Math.cos(atan);
/* 四個點 */
float headerX1 = mHeaderCircle.x - mCurrentRadius * sin;
float headerY1 = mHeaderCircle.y + mCurrentRadius * cos;
float headerX2 = mHeaderCircle.x + mCurrentRadius * sin;
float headerY2 = mHeaderCircle.y - mCurrentRadius * cos;
float footerX1 = mFooterCircle.x - mFooterCircle.radius * sin;
float footerY1 = mFooterCircle.y + mFooterCircle.radius * cos;
float footerX2 = mFooterCircle.x + mFooterCircle.radius * sin;
float footerY2 = mFooterCircle.y - mFooterCircle.radius * cos;
/* 控制點 */
float anchorX = ( mHeaderCircle.x + mFooterCircle.x ) / 2;
float anchorY = ( mHeaderCircle.y + mFooterCircle.y ) / 2;
/* 畫貝塞爾曲線 */
mPath.reset();
mPath.moveTo(headerX1, headerY1);
mPath.quadTo(anchorX, anchorY, footerX1, footerY1);
mPath.lineTo(footerX2, footerY2);
mPath.quadTo(anchorX, anchorY, headerX2, headerY2);
mPath.lineTo(headerX1, headerY1);
canvas.drawPath(mPath, mPaint);
}
8執行示例
9結語
聽說這個功能的效果實現,經過了開發與產品多次腦暴與打磨。之所以受使用者喜愛,除了實用外,更重要的是實現效果讓使用者覺得更有趣,更靈動,也更軟性。而且這個效果其實可以適用於多種場景。
騰訊Bugly 最專業的質量跟蹤平臺
精神哥、小蘿莉,為您定期分享應用崩潰解決方案