Android未讀消息拖動(dòng)氣泡示例代碼詳解(附源碼)
前言
拖動(dòng)清除未讀消息可以說在很多應(yīng)用中都很常見,也被用戶廣泛接受。本文是一個(gè)可以供參考的Demo,希望能有幫助。
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
最終效果圖及思路


實(shí)現(xiàn)關(guān)鍵:
氣泡中間的兩條邊,分別是以ab,cd為數(shù)據(jù)點(diǎn),G為控制點(diǎn)的貝塞爾曲線。
步驟:
繪制圓背景以及文本;連接情況繪制貝塞爾曲線;另外端點(diǎn)繪制一個(gè)圓
關(guān)鍵代碼
1.定義,初始化等
狀態(tài):靜止、連接、分離、消失
在onSizeChanged中初始化狀態(tài),固定氣泡以及可動(dòng)氣泡的圓心
代碼如下(示例):
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
init(w, h);
}
private void init(int w, int h) {
mBubbleState = BUBBLE_STATE_DEFAULT;
//設(shè)置固定氣泡圓心初始坐標(biāo)
if (mBubFixedCenter == null) {
mBubFixedCenter = new PointF(w / 2, h / 2);
} else {
mBubFixedCenter.set(w / 2, h / 2);
}
//設(shè)置可動(dòng)氣泡圓心初始坐標(biāo)
if (mBubMovableCenter == null) {
mBubMovableCenter = new PointF(w / 2, h / 2);
} else {
mBubMovableCenter.set(w / 2, h / 2);
}
}
2.onDraw中繪制包括三樣繪制
第一樣:靜止,連接,分離狀態(tài)都需要繪制圓背景以及文本:
//靜止,連接,分離狀態(tài)都需要繪制圓背景以及文本
if (mBubbleState != BUBBLE_STATE_DISMISS) {
canvas.drawCircle(mBubMovableCenter.x, mBubMovableCenter.y, mBubMovableRadius, mBubblePaint);
mTextPaint.getTextBounds(mTextStr, 0, mTextStr.length(), mTextRect);
canvas.drawText(mTextStr, mBubMovableCenter.x - mTextRect.width() / 2, mBubMovableCenter.y + mTextRect.height() / 2, mTextPaint);
}
第二樣:連接狀態(tài)繪制貝塞爾曲線①。
if (mBubbleState == BUBBLE_STATE_CONNECT) {
//繪制靜止的氣泡
canvas.drawCircle(mBubFixedCenter.x, mBubFixedCenter.y, mBubFixedRadius, mBubblePaint);
//計(jì)算控制點(diǎn)的坐標(biāo)
int iAnchorX = (int) ((mBubMovableCenter.x + mBubFixedCenter.x) / 2);
int iAnchorY = (int) ((mBubMovableCenter.y + mBubFixedCenter.y) / 2);
float sinTheta = (mBubMovableCenter.y - mBubFixedCenter.y) / mDist;
float cosTheta = (mBubMovableCenter.x - mBubFixedCenter.x) / mDist;
//D
float iBubFixedStartX = mBubFixedCenter.x - mBubFixedRadius * sinTheta;
float iBubFixedStartY = mBubFixedCenter.y + mBubFixedRadius * cosTheta;
//C
float iBubMovableEndX = mBubMovableCenter.x - mBubMovableRadius * sinTheta;
float iBubMovableEndY = mBubMovableCenter.y + mBubMovableRadius * cosTheta;
//A
float iBubFixedEndX = mBubFixedCenter.x + mBubFixedRadius * sinTheta;
float iBubFixedEndY = mBubFixedCenter.y - mBubFixedRadius * cosTheta;
//B
float iBubMovableStartX = mBubMovableCenter.x + mBubMovableRadius * sinTheta;
float iBubMovableStartY = mBubMovableCenter.y - mBubMovableRadius * cosTheta;
mBezierPath.reset();
mBezierPath.moveTo(iBubFixedStartX, iBubFixedStartY);
mBezierPath.quadTo(iAnchorX, iAnchorY, iBubMovableEndX, iBubMovableEndY);
mBezierPath.lineTo(iBubMovableStartX, iBubMovableStartY);
mBezierPath.quadTo(iAnchorX, iAnchorY, iBubFixedEndX, iBubFixedEndY);
mBezierPath.close();
canvas.drawPath(mBezierPath, mBubblePaint);
}
第三樣:消失狀態(tài)執(zhí)行爆炸動(dòng)畫
// 認(rèn)為是消失狀態(tài),執(zhí)行爆炸動(dòng)畫
if (mBubbleState == BUBBLE_STATE_DISMISS && mCurDrawableIndex < mBurstBitmapsArray.length) {
mBurstRect.set(
(int) (mBubMovableCenter.x - mBubMovableRadius),
(int) (mBubMovableCenter.y - mBubMovableRadius),
(int) (mBubMovableCenter.x + mBubMovableRadius),
(int) (mBubMovableCenter.y + mBubMovableRadius));
canvas.drawBitmap(mBurstBitmapsArray[mCurDrawableIndex], null, mBurstRect, mBubblePaint);
}
3.onTouchEvent中
按下:區(qū)分靜止?fàn)顟B(tài)和連接狀態(tài)
case MotionEvent.ACTION_DOWN:
if (mBubbleState != BUBBLE_STATE_DISMISS) {
mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
if (mDist < mBubbleRadius + MOVE_OFFSET) {
//加上MOVE_OFFSET是為了方便拖拽
mBubbleState = BUBBLE_STATE_CONNECT;
} else {
mBubbleState = BUBBLE_STATE_DEFAULT;
}
}
break;
移動(dòng):判斷是否到了分離狀態(tài)
case MotionEvent.ACTION_MOVE:
if (mBubbleState != BUBBLE_STATE_DEFAULT) {
mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
mBubMovableCenter.x = event.getX();
mBubMovableCenter.y = event.getY();
if (mBubbleState == BUBBLE_STATE_CONNECT) {
if (mDist < mMaxDist - MOVE_OFFSET) {
mBubFixedRadius = mBubbleRadius - mDist / 8;
} else {
mBubbleState = BUBBLE_STATE_APART;
}
}
invalidate();
}
break;
彈起:判斷是否已經(jīng)到了分離狀態(tài),分離狀態(tài)爆炸,未分離反彈
case MotionEvent.ACTION_UP:
if (mBubbleState == BUBBLE_STATE_CONNECT) {
// 橡皮筋動(dòng)畫
startBubbleRestAnim();
} else if (mBubbleState == BUBBLE_STATE_APART) {
if (mDist < 2 * mBubbleRadius){
//反彈動(dòng)畫
startBubbleRestAnim();
}else{
// 爆炸動(dòng)畫
startBubbleBurstAnim();
}
}
break;
4.反彈和爆炸動(dòng)畫
/**
* 連接狀態(tài)下松開手指,執(zhí)行類似橡皮筋動(dòng)畫
*/
private void startBubbleRestAnim() {
ValueAnimator anim = ValueAnimator.ofObject(new PointFEvaluator(),
new PointF(mBubMovableCenter.x, mBubMovableCenter.y),
new PointF(mBubFixedCenter.x, mBubFixedCenter.y));
anim.setDuration(200);
anim.setInterpolator(new OvershootInterpolator(5f));
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBubMovableCenter = (PointF) animation.getAnimatedValue();
invalidate();
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mBubbleState = BUBBLE_STATE_DEFAULT;
}
});
anim.start();
}
/**
* 爆炸動(dòng)畫
*/
private void startBubbleBurstAnim() {
//將氣泡改成消失狀態(tài)
mBubbleState = BUBBLE_STATE_DISMISS;
ValueAnimator animator = ValueAnimator.ofInt(0, mBurstBitmapsArray.length);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurDrawableIndex = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
總結(jié)
注:①貝塞爾曲線參考博文
本文完,有需要參考的同學(xué)→文中Demo下載地址
本系列文章引導(dǎo)頁點(diǎn)擊這里
到此這篇關(guān)于Android未讀消息拖動(dòng)氣泡示例代碼詳解的文章就介紹到這了,更多相關(guān)Android未讀消息拖動(dòng)氣泡內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 不同Activity間數(shù)據(jù)的傳遞 Bundle對(duì)象的應(yīng)用
本篇文章小編為大家介紹,Android 不同Activity間數(shù)據(jù)的傳遞 Bundle對(duì)象的應(yīng)用。需要的朋友參考下2013-04-04
Android UI自定義ListView實(shí)現(xiàn)下拉刷新和加載更多效果
這篇文章主要介紹了Android UI自定義ListView實(shí)現(xiàn)下拉刷新和加載更多效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
PopupWindow自定義位置顯示的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了PopupWindow自定義位置顯示,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Android實(shí)現(xiàn)跟隨手指拖動(dòng)并自動(dòng)貼邊的View樣式(實(shí)例demo)
本文通過實(shí)例代碼給大家介紹了android實(shí)現(xiàn)跟隨手指拖動(dòng)并自動(dòng)貼邊的View樣式,效果非常棒,具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01
Android Jetpack架構(gòu)組件Lifecycle詳解
這篇文章主要介紹了Android Jetpack架構(gòu)組件Lifecycle詳解,Lifecycle是Jetpack架構(gòu)組件中用來感知生命周期的組件,使用Lifecycles可以幫助我們寫出和生命周期相關(guān)更簡(jiǎn)潔更易維護(hù)的代碼。對(duì)此感興趣的小伙伴可以來學(xué)習(xí)一下2020-07-07

