上一篇介紹了貝塞爾曲線的簡單應用?[仿360內存清理效果](http://blog.csdn.net/wingichoy/article/details/50500479)
這一篇帶來一個 ?兩條貝塞爾曲線的應用 :?仿qq未讀消息去除效果。
轉載請注明出處:http://blog.csdn.net/wingichoy/article/details/50503630
老規矩,先上效果圖:

qq的未讀消息去除很炫酷,其實就是用了兩條貝塞爾曲線,我們按思路來,先來畫兩個圓,及兩條貝塞爾曲線,輔助點為圓心y坐標的一半。我們把下面移動的圓,叫做mMoveCircle.

這樣一畫,就很簡單明了了對不對。只要在拖動的時候 去改變輔助點的Y,和固定圓的半徑, 就可以出來如效果圖的效果。
那么重寫onTouchEvent
~~~
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mMoveCircleY = event.getY();
// mSupY = mCircleY;
if (mMoveCircleY < lastY) {
isUp = false;
} else {
isUp = true;
}
lastY = mMoveCircleY;
invalidate();
break;
case MotionEvent.ACTION_UP:
if((mMoveCircleY-mCircleY)>mMoveCircleRadius*3){
Log.e("wing","超過");
isCanDraw = false;
invalidate();
}else {
Log.e("wing","沒超過");
}
}
return true;
}
~~~
這里Action_move記錄了最后一次移動和這次移動的值,來代表是往上滑還是往下滑,改變一個標記為,并且讓mMoveCircleY改變為觸摸的y坐標。
ACTION_UP主要記錄 圓點離開的范圍,如果超過這個范圍,則代表消息提醒去除掉。
在ondraw里繪制貝塞爾曲線
~~~
//左邊的線
path.moveTo(mCircleX - mCircleRadius + mPaintStrokeWidth / 2, mCircleY);
path.quadTo(mCircleX , mSupY, mCircleX - mMoveCircleRadius + mPaintStrokeWidth / 2, mMoveCircleY);
canvas.drawPath(path, p);
//右邊的線
path.moveTo(mCircleX + mCircleRadius - mPaintStrokeWidth / 2, mCircleY);
path.quadTo(mCircleX, mSupY, mCircleX + mMoveCircleRadius - mPaintStrokeWidth / 2, mMoveCircleY);
canvas.drawPath(path, p);
~~~
這樣可以畫成兩條貝塞爾曲線,但是是空心的

那么我們想象辦法,如何才能讓他封閉呢。還記得繪制多邊形的辦法嗎。。直接在兩條貝塞爾曲線之間lineto就可以了,然后close;
~~~
path.moveTo(mCircleX - mCircleRadius + mPaintStrokeWidth / 2, mCircleY);
path.quadTo(mCircleX , mSupY, mCircleX - mMoveCircleRadius + mPaintStrokeWidth / 2, mMoveCircleY);
path.lineTo(mCircleX + mMoveCircleRadius, mMoveCircleY);
path.quadTo(mCircleX, mSupY, mCircleX + mCircleRadius, mCircleY);
path.lineTo(mCircleX - mCircleRadius, mCircleY);
path.close();
canvas.drawPath(path, p);
~~~
其實我們繪制的是這個效果,然后在把兩個實心圓畫上去,畫筆style設置為fill 即可、

封閉畫筆,動態改變半徑,繪制圓。
~~~
p.setStyle(Paint.Style.FILL);
~~~
~~~
if (isUp) {
canvas.drawCircle(mCircleX, mCircleY, mCircleRadius--, p);
canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
} else {
canvas.drawCircle(mCircleX, mCircleY, mCircleRadius++, p);
canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
}
~~~
得到如下效果。

最后,來判斷是否超過一定下拉區域,如果超過 則圓點跟著手一起走 然后在手松開的時候消失。
~~~
if ((mMoveCircleY-mCircleY)<mMoveCircleRadius*3) {
Log.e("wing",mSupY-mCircleY+"");
Path path = new Path();
//左邊的線
path.moveTo(mCircleX - mCircleRadius + mPaintStrokeWidth / 2, mCircleY);
path.quadTo(mCircleX , mSupY, mCircleX - mMoveCircleRadius + mPaintStrokeWidth / 2, mMoveCircleY);
path.lineTo(mCircleX + mMoveCircleRadius, mMoveCircleY);
path.quadTo(mCircleX, mSupY, mCircleX + mCircleRadius, mCircleY);
path.lineTo(mCircleX - mCircleRadius, mCircleY);
path.close();
canvas.drawPath(path, p);
//右邊的線
// path.moveTo(mCircleX + mCircleRadius - mPaintStrokeWidth / 2, mCircleY);
// path.quadTo(mCircleX, mSupY, mCircleX + mMoveCircleRadius - mPaintStrokeWidth / 2, mMoveCircleY);
// canvas.drawPath(path, p);
p.setStyle(Paint.Style.FILL);
// canvas.drawCircle(mCircleX, mCircleY, mCircleRadius, p);
if (isUp) {
canvas.drawCircle(mCircleX, mCircleY, mCircleRadius--, p);
canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
} else {
canvas.drawCircle(mCircleX, mCircleY, mCircleRadius++, p);
canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
}
// canvas.drawPoint(mSupX, mSupY, p);
} else {
p.setStyle(Paint.Style.FILL);
canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
// canvas.drawCircle(mCircleX, mMoveCircleY, mMoveCircleRadius, p);
}
~~~
最后遺留一個復雜的問題, 用戶可以360度拖動,這時候要根據圓 來計算切點的坐標,再來重新計算輔助點的坐標。比較繁瑣,這里就不算了。 #其實我是不會#本篇的目的,了解貝塞爾曲線的應用我想已經達到了,讀者若感興趣,可以自行實現。#其實我是不會#
------------------------------------------------------------------------------解決篇-----------------------------------------------------------------------------------------------------
回到家又研究了一下360度動態轉變的,終于想通怎么算了。如下圖(無視那么丑 還有想吐槽下中性筆的油):

其實我們要面臨的問題就是計算出 圓的切點坐標。 ?換言之就是求 ∠1 的角度。
由圖可知 ∠1 = ∠2 ? ? ∠2 ?= ∠3 ?由于等價代換可得 ∠3 = ∠1
由圖得:tan∠3 = (mCirlceY - mMoveCircleY)/(mMoveCircleX-mCircleX) ?即∠1 = arctan?(mCirlceY - mMoveCircleY)/(mMoveCircleX-mCircleX
則可計算右上點的坐標為:
x: mMoveCircleX - mMoveCircleRadius * sin∠1
y: mMoveCircleY - mMoveCircleRadius * cos∠1
其他三個切線點的坐標計算方法如上。得到四個點的坐標,只需要改變path的起終點 和 輔助點的坐標即可。由于過于繁雜,就不用代碼給各位實現了。大家如果感興趣,可以自行實現。#其實就是懶#?
下一篇:[wing帶你玩轉自定義view系列(3)模仿微信下拉眼睛](http://blog.csdn.net/wingichoy/article/details/50503858)
本項目地址:[點擊打開鏈接](https://github.com/githubwing/QQMessage)? ?求關注 求評論 ?求star
- 前言
- android自定義viewgroup初步之一----抽屜菜單
- Android 自定義view --圓形百分比(進度條)
- Android 自定義View -- 簡約的折線圖
- 新手自定義view練習實例之(一) 泡泡彈窗
- 新手自定義view練習實例之(二) 波浪view
- 手把手帶你畫一個 時尚儀表盤 Android 自定義View
- 手把手帶你畫一個動態錯誤提示 Android自定義view
- 手把手帶你做一個超炫酷loading成功動畫view Android自定義view
- 關于Android自定義view 你所需要知道的基本函數
- Android自定義view進階-- 神奇的貝塞爾曲線
- wing帶你玩轉自定義view系列(1) 仿360內存清理效果
- wing帶你玩轉自定義view系列(2) 簡單模仿qq未讀消息去除效果
- wing帶你玩轉自定義view系列(3)模仿微信下拉眼睛
- 手把手教你畫一個 逼格滿滿圓形水波紋loadingview Android
- 有坑?? 為何wing墜入PorterDuffXferMode的萬丈深淵(PorterDuffXferMode深入試驗)
- 手把手帶你畫一個漂亮蜂窩view Android自定義view
- 一個炫字都不夠??!!!手把手帶你打造3D自定義view
- 恭喜發財! -- 手把手教你仿造一個qq下拉搶紅包 Android自定義view