Android高仿QQ小紅點(diǎn)功能
先給大家展示下效果圖:

代碼已上傳至Github:高仿QQ小紅點(diǎn),如對您有幫助,歡迎star~感謝
繪制貝塞爾曲線:

主要是當(dāng)在一定范圍內(nèi)拖拽時算出固定圓和拖拽圓的外切直線以及對應(yīng)的切點(diǎn),就可以通過path.quadTo()來繪制二階貝塞爾曲線了~
整體思路:
1、當(dāng)小紅點(diǎn)靜止時,什么都不做,只需要給自定義小紅點(diǎn)QQBezierView(extends TextView)添加一個.9文件當(dāng)背景即可
2、當(dāng)滑動時,通過getRootView()獲得頂根View,然后new一個DragView ( extends View ) 來繪制各種狀態(tài)時的小紅點(diǎn),并且通過getRootView().addView()的方式把DragView 加進(jìn)去,這樣DragView 就可以實(shí)現(xiàn)全屏滑動了
實(shí)現(xiàn)過程:
自定義QQBezierView ( extends TextView ) 并復(fù)寫onTouchEvent來處理各種情況,代碼如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
//獲得根View
View rootView = getRootView();
//獲得觸摸位置在全屏所在位置
float mRawX = event.getRawX();
float mRawY = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//請求父View不攔截
getParent().requestDisallowInterceptTouchEvent(true);
//獲得當(dāng)前View在屏幕上的位置
int[] cLocation = new int[2];
getLocationOnScreen(cLocation);
if (rootView instanceof ViewGroup) {
//初始化拖拽時顯示的View
dragView = new DragView(getContext());
//設(shè)置固定圓的圓心坐標(biāo)
dragView.setStickyPoint(cLocation[0] + mWidth / 2, cLocation[1] + mHeight / 2, mRawX, mRawY);
//獲得緩存的bitmap,滑動時直接通過drawBitmap繪制出來
setDrawingCacheEnabled(true);
Bitmap bitmap = getDrawingCache();
if (bitmap != null) {
dragView.setCacheBitmap(bitmap);
//將DragView添加到RootView中,這樣就可以全屏滑動了
((ViewGroup) rootView).addView(dragView);
setVisibility(INVISIBLE);
}
}
break;
case MotionEvent.ACTION_MOVE:
//請求父View不攔截
getParent().requestDisallowInterceptTouchEvent(true);
if (dragView != null) {
//更新DragView的位置
dragView.setDragViewLocation(mRawX, mRawY);
}
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
if (dragView != null) {
//手抬起時來判斷各種情況
dragView.setDragUp();
}
break;
}
return true;
}上面代碼注釋已經(jīng)很詳細(xì)了,總結(jié)一下就是通過內(nèi)部攔截法來請求父View是否攔截分發(fā)事件,并通過event.getRawX()和event.getRawY()來不斷更新DragView的位置,那么DragView都做了哪些事呢,接下來就看一下DragView,DragView是QQBezierView 的一個內(nèi)部View類:
private int mState;//當(dāng)前紅點(diǎn)的狀態(tài) private static final int STATE_INIT = 0;//默認(rèn)靜止?fàn)顟B(tài) private static final int STATE_DRAG = 1;//拖拽狀態(tài) private static final int STATE_MOVE = 2;//移動狀態(tài) private static final int STATE_DISMISS = 3;//消失狀態(tài)
首先聲明了小紅點(diǎn)的四種狀態(tài),靜止?fàn)顟B(tài),拖拽狀態(tài),移動狀態(tài)和消失狀態(tài)。
在QQBezierView 的onTouchEvent的DOWN事件中調(diào)用了setStickyPoint()方法:
/**
* 設(shè)置固定圓的圓心和半徑
* @param stickyX 固定圓的X坐標(biāo)
* @param stickyY 固定圓的Y坐標(biāo)
*/
public void setStickyPoint(float stickyX, float stickyY, float touchX, float touchY) {
//分別設(shè)置固定圓和拖拽圓的坐標(biāo)
stickyPointF.set(stickyX, stickyY);
dragPointF.set(touchX, touchY);
//通過兩個圓點(diǎn)算出圓心距,也是拖拽的距離
dragDistance = MathUtil.getTwoPointDistance(dragPointF, stickyPointF);
if (dragDistance <= maxDistance) {
//如果拖拽距離小于規(guī)定最大距離,則固定的圓應(yīng)該越來越小,這樣看著才符合實(shí)際
stickRadius = (int) (defaultRadius - dragDistance / 10) < 10 ? 10 : (int) (defaultRadius - dragDistance / 10);
mState = STATE_DRAG;
} else {
mState = STATE_INIT;
}
}接著,在QQBezierView 的onTouchEvent的MOVE事件中調(diào)用了setDragViewLocation()方法:
/**
* 設(shè)置拖拽的坐標(biāo)位置
*
* @param touchX 拖拽時的X坐標(biāo)
* @param touchY 拖拽時的Y坐標(biāo)
*/
public void setDragViewLocation(float touchX, float touchY) {
dragPointF.set(touchX, touchY);
//隨時更改圓心距
dragDistance = MathUtil.getTwoPointDistance(dragPointF, stickyPointF);
if (mState == STATE_DRAG) {
if (isInsideRange()) {
stickRadius = (int) (defaultRadius - dragDistance / 10) < 10 ? 10 : (int) (defaultRadius - dragDistance / 10);
} else {
mState = STATE_MOVE;
if (onDragListener != null) {
onDragListener.onMove();
}
}
}
invalidate();
}最后在QQBezierView 的onTouchEvent的UP事件中調(diào)用了setDragUp()方法:
public void setDragUp() {
if (mState == STATE_DRAG && isInsideRange()) {
//拖拽狀態(tài)且在范圍之內(nèi)
startResetAnimator();
} else if (mState == STATE_MOVE) {
if (isInsideRange()) {
//在范圍之內(nèi) 需要RESET
startResetAnimator();
} else {
//在范圍之外 消失動畫
mState = STATE_DISMISS;
startExplodeAnim();
}
}
}最后來看下DragView的onDraw方法,拖拽時的貝塞爾曲線以及拖拽滑動時的狀態(tài)都是通過onDraw實(shí)現(xiàn)的:
@Override
protected void onDraw(Canvas canvas) {
if (isInsideRange() && mState == STATE_DRAG) {
mPaint.setColor(Color.RED);
//繪制固定的小圓
canvas.drawCircle(stickyPointF.x, stickyPointF.y, stickRadius, mPaint);
//首先獲得兩圓心之間的斜率
Float linK = MathUtil.getLineSlope(dragPointF, stickyPointF);
//然后通過兩個圓心和半徑、斜率來獲得外切線的切點(diǎn)
PointF[] stickyPoints = MathUtil.getIntersectionPoints(stickyPointF, stickRadius, linK);
dragRadius = (int) Math.min(mWidth, mHeight) / 2;
PointF[] dragPoints = MathUtil.getIntersectionPoints(dragPointF, dragRadius, linK);
mPaint.setColor(Color.RED);
//二階貝塞爾曲線的控制點(diǎn)取得兩圓心的中點(diǎn)
controlPoint = MathUtil.getMiddlePoint(dragPointF, stickyPointF);
//繪制貝塞爾曲線
mPath.reset();
mPath.moveTo(stickyPoints[0].x, stickyPoints[0].y);
mPath.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
mPath.lineTo(dragPoints[1].x, dragPoints[1].y);
mPath.quadTo(controlPoint.x, controlPoint.y, stickyPoints[1].x, stickyPoints[1].y);
mPath.lineTo(stickyPoints[0].x, stickyPoints[0].y);
canvas.drawPath(mPath, mPaint);
}
if (mCacheBitmap != null && mState != STATE_DISMISS) {
//繪制緩存的Bitmap
canvas.drawBitmap(mCacheBitmap, dragPointF.x - mWidth / 2,
dragPointF.y - mHeight / 2, mPaint);
}
if (mState == STATE_DISMISS && explodeIndex < explode_res.length) {
//繪制小紅點(diǎn)消失時的爆炸動畫
canvas.drawBitmap(bitmaps[explodeIndex], dragPointF.x - mWidth / 2, dragPointF.y - mHeight / 2, mPaint);
}
}PS:最開始使用的是 Android:clipChildren=”false” 這個屬性,如果父View只是一個普通的ViewGroup(如LinearLayout、RelativeLayout等),此時在父View中設(shè)置android:clipChildren=”false”后,子View就可以超出自己的范圍,在ViewGroup中也可以滑動了,此時也沒問題;但是當(dāng)是RecycleView時,只要ItemView設(shè)置了background屬性,滑動時的DragView就會顯示在background的下面了,如有知其原因的還望不吝賜教~
最后再貼下源碼下載地址:Android高仿QQ小紅點(diǎn)
以上所述是小編給大家介紹的Android高仿QQ小紅點(diǎn)功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
android開發(fā)教程之framework增加字符串資源和圖片等resource資源
這篇文章主要介紹了android開發(fā)中framework增加字符串資源和圖片等resource資源方法,需要的朋友可以參考下2014-02-02
android模擬器開發(fā)和測試nfc應(yīng)用實(shí)例詳解
本文介紹android模擬器開發(fā)nfc應(yīng)用詳解,大家參考使用吧2013-12-12
Android獲取和讀取短信驗證碼的實(shí)現(xiàn)方法
這篇文章主要介紹了Android獲取和讀取短信驗證碼的實(shí)現(xiàn)方法,文章內(nèi)容介紹了如何讀取剛收到的短信的相關(guān)內(nèi)容,以及Android獲取短信驗證碼的方法,感興趣的小伙伴們可以參考一下2016-03-03
Android Studio 運(yùn)行按鈕灰色的完美解決方法
今天新建項目的時候突然發(fā)現(xiàn)編譯后運(yùn)行按鈕為灰色,今天小編給大家?guī)砹薃ndroid Studio 運(yùn)行按鈕灰色的完美解決方法,非常不錯,對大家的需要或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-10-10
Android自定義view利用PathEffect實(shí)現(xiàn)動態(tài)效果
這篇文章主要為大家詳細(xì)介紹了Android自定義view利用PathEffect實(shí)現(xiàn)動態(tài)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05
Android使用animator實(shí)現(xiàn)fragment的3D翻轉(zhuǎn)效果
這篇文章主要為大家詳細(xì)介紹了Android使用animator實(shí)現(xiàn)fragment的3D翻轉(zhuǎn)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12

