版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失
本篇文章為大家展示了怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。分析(用到的知識點(diǎn)):
(1)ValueAnimator(數(shù)值生成器)用于生成數(shù)值,可以設(shè)置差值器來改變數(shù)字的變化幅度。(2)ObjectAnimator(動畫生成器)用于生成各種屬性,布局動畫,同樣也可以設(shè)置差值器來改變效果。(3)貝塞爾一階曲線(4)自定義View的基礎(chǔ)知識(5)WindowManager使view拖拽能顯示在整個屏幕的任何地方,而不是局限于父布局內(nèi)具體實(shí)現(xiàn)方法一、首先我們要實(shí)現(xiàn)基礎(chǔ)效果基礎(chǔ)效果是點(diǎn)擊屏幕任意一點(diǎn)能出現(xiàn)消息拖拽的效果,但是此時我們不用管我們拖動的View,只需要完成大致模型。該部分的難點(diǎn)在于貝塞爾一階曲線的怎么實(shí)現(xiàn)。基礎(chǔ)效果圖
分析:(1)點(diǎn)擊任意一點(diǎn)畫出兩個圓,和一個有貝塞爾曲線組成的path路徑(2)隨著拖動距離的增加原點(diǎn)的圓半徑逐漸縮小,當(dāng)距離達(dá)到一定大以后原點(diǎn)的圓和貝塞爾曲線組成的path不再顯示貝塞爾曲線的畫法首先我們需要求出角a的大小,根據(jù)角a來求到A,B,C,D的坐標(biāo)位子,然后求到控制點(diǎn)E點(diǎn)的坐標(biāo),通過Path.quadTo()方法來連接A,B和C,D兩條貝塞爾曲線。各點(diǎn)坐標(biāo)A(c1.x+sina*c1半徑,c1.y-cina*c1半徑)B(c2.x+sina*c2半徑,c2.y-cina*c2半徑)C(c2.x-sina*c1半徑,c2.y+cina*c1半徑)D(c1.x-sina*c2半徑,c1.y+cina*c2半徑)E((c1.x+c2.x)/2,(c1.y+c2.y)/2)A(c1.x+sina*c1半徑,c1.y-cina*c1半徑)B(c2.x+sina*c2半徑,c2.y-cina*c2半徑)C(c2.x-sina*c1半徑,c2.y+cina*c1半徑)D(c1.x-sina*c2半徑,c1.y+cina*c2半徑)E((c1.x+c2.x)/2,(c1.y+c2.y)/2)貝塞爾曲線的path代碼private
Path
getBezeierPath()
{
double
distance
=
getDistance(mBigCirclePoint,mLittleCirclePoint);
mLittleCircleRadius
=
(int)
(mLittleCircleRadiusMax
-
distance
/
10);
if
(mLittleCircleRadius
<
mLittleCircleRadiusMin)
{
//
超過一定距離
貝塞爾和固定圓都不要畫了
return
null;
}
Path
bezeierPath
=
new
Path();
//
求角
a
//
求斜率
float
dy
=
(mBigCirclePoint.y-mLittleCirclePoint.y);
float
dx
=
(mBigCirclePoint.x-mLittleCirclePoint.x);
float
tanA
=
dy/dx;
//
求角a
double
arcTanA
=
Math.atan(tanA);
//
A
float
Ax
=
(float)
(mLittleCirclePoint.x
+
mLittleCircleRadius*Math.sin(arcTanA));
float
Ay
=
(float)
(mLittleCirclePoint.y
-
mLittleCircleRadius*Math.cos(arcTanA));
//
B
float
Bx
=
(float)
(mBigCirclePoint.x
+
mBigCircleRadius*Math.sin(arcTanA));
float
By
=
(float)
(mBigCirclePoint.y
-
mBigCircleRadius*Math.cos(arcTanA));
//
C
float
Cx
=
(float)
(mBigCirclePoint.x
-
mBigCircleRadius*Math.sin(arcTanA));
float
Cy
=
(float)
(mBigCirclePoint.y
+
mBigCircleRadius*Math.cos(arcTanA));
//
D
float
Dx
=
(float)
(mLittleCirclePoint.x
-
mLittleCircleRadius*Math.sin(arcTanA));
float
Dy
=
(float)
(mLittleCirclePoint.y
+
mLittleCircleRadius*Math.cos(arcTanA));
//
拼裝
貝塞爾的曲線路徑
bezeierPath.moveTo(Ax,Ay);
//
移動
//
兩個點(diǎn)
PointF
controlPoint
=
getControlPoint();
//
畫了第一條
第一個點(diǎn)(控制點(diǎn),兩個圓心的中心點(diǎn)),終點(diǎn)
bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);
//
畫第二條
bezeierPath.lineTo(Cx,Cy);
//
鏈接到
bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);
bezeierPath.close();
return
bezeierPath;
}
二、完善代碼
這部分我們需要完善所有代碼,實(shí)現(xiàn)代碼的分離,使得所用View都能被拖動,且需要創(chuàng)建一個監(jiān)聽器來監(jiān)聽View是否拖動結(jié)束了,結(jié)束后調(diào)用回調(diào)方法以便需要做其他處理。需要完成的功能:(1)將傳入的View畫出來(2)在手指抬起時判斷是爆炸還是回彈(3)完成回彈和爆炸的代碼部分(4)回彈或者爆炸結(jié)束后調(diào)用回調(diào)通知動畫結(jié)束(5)使用WindowManager把自定義拖拽View加進(jìn)去,隱藏原來得View實(shí)現(xiàn)View在任意地方拖動完整代碼部分(1)自定義View的代碼public
class
MsgDrafitingView
extends
View{
private
PointF
mLittleCirclePoint;
private
PointF
mBigCirclePoint;
private
Paint
mPaint;
//大圓半徑
private
int
mBigCircleRadius
=
10;
//小圓半徑
private
int
mLittleCircleRadiusMax
=
10;
private
int
mLittleCircleRadiusMin
=
2;
private
int
mLittleCircleRadius;
private
Bitmap
dragBitmap;
private
OnToucnUpListener
mOnToucnUpListener;
public
MsgDrafitingView(Context
context)
{
this(context,null);
}
public
MsgDrafitingView(Context
context,
@Nullable
AttributeSet
attrs)
{
this(context,
attrs,0);
}
public
MsgDrafitingView(Context
context,
@Nullable
AttributeSet
attrs,
int
defStyleAttr)
{
super(context,
attrs,
defStyleAttr);
mBigCircleRadius
=
dip2px(mBigCircleRadius);
mLittleCircleRadiusMax
=
dip2px(mLittleCircleRadiusMax);
mLittleCircleRadiusMin
=
dip2px(mLittleCircleRadiusMin);
mPaint
=
new
Paint();
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
}
private
int
dip2px(int
dip)
{
return
(int)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
protected
void
onDraw(Canvas
canvas)
{
if
(mBigCirclePoint
==
null
||
mLittleCirclePoint
==
null)
{
return;
}
//畫大圓
canvas.drawCircle(mBigCirclePoint.x,
mBigCirclePoint.y,
mBigCircleRadius,
mPaint);
//獲得貝塞爾路徑
Path
bezeierPath
=
getBezeierPath();
if
(bezeierPath!=null)
{
//
小到一定層度就不見了(不畫了)
canvas.drawCircle(mLittleCirclePoint.x,
mLittleCirclePoint.y,
mLittleCircleRadius,
mPaint);
//
畫貝塞爾曲線
canvas.drawPath(bezeierPath,
mPaint);
}
//
畫圖片
if
(dragBitmap
!=
null)
{
canvas.drawBitmap(dragBitmap,
mBigCirclePoint.x
-
dragBitmap.getWidth()
/
2,
mBigCirclePoint.y
-
dragBitmap.getHeight()
/
2,
null);
}
}
private
Path
getBezeierPath()
{
double
distance
=
getDistance(mBigCirclePoint,mLittleCirclePoint);
mLittleCircleRadius
=
(int)
(mLittleCircleRadiusMax
-
distance
/
10);
if
(mLittleCircleRadius
<
mLittleCircleRadiusMin)
{
//
超過一定距離
貝塞爾和固定圓都不要畫了
return
null;
}
Path
bezeierPath
=
new
Path();
//
求角
a
//
求斜率
float
dy
=
(mBigCirclePoint.y-mLittleCirclePoint.y);
float
dx
=
(mBigCirclePoint.x-mLittleCirclePoint.x);
float
tanA
=
dy/dx;
//
求角a
double
arcTanA
=
Math.atan(tanA);
//
A
float
Ax
=
(float)
(mLittleCirclePoint.x
+
mLittleCircleRadius*Math.sin(arcTanA));
float
Ay
=
(float)
(mLittleCirclePoint.y
-
mLittleCircleRadius*Math.cos(arcTanA));
//
B
float
Bx
=
(float)
(mBigCirclePoint.x
+
mBigCircleRadius*Math.sin(arcTanA));
float
By
=
(float)
(mBigCirclePoint.y
-
mBigCircleRadius*Math.cos(arcTanA));
//
C
float
Cx
=
(float)
(mBigCirclePoint.x
-
mBigCircleRadius*Math.sin(arcTanA));
float
Cy
=
(float)
(mBigCirclePoint.y
+
mBigCircleRadius*Math.cos(arcTanA));
//
D
float
Dx
=
(float)
(mLittleCirclePoint.x
-
mLittleCircleRadius*Math.sin(arcTanA));
float
Dy
=
(float)
(mLittleCirclePoint.y
+
mLittleCircleRadius*Math.cos(arcTanA));
//
拼裝
貝塞爾的曲線路徑
bezeierPath.moveTo(Ax,Ay);
//
移動
//
兩個點(diǎn)
PointF
controlPoint
=
getControlPoint();
//
畫了第一條
第一個點(diǎn)(控制點(diǎn),兩個圓心的中心點(diǎn)),終點(diǎn)
bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);
//
畫第二條
bezeierPath.lineTo(Cx,Cy);
//
鏈接到
bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);
bezeierPath.close();
return
bezeierPath;
}
/**
*
獲得控制點(diǎn)距離
*/
public
PointF
getControlPoint()
{
return
new
PointF((mLittleCirclePoint.x+mBigCirclePoint.x)/2,(mLittleCirclePoint.y+mBigCirclePoint.y)/2);
}
/**
*
獲得兩點(diǎn)之間的距離
*/
private
double
getDistance(PointF
point1,
PointF
point2)
{
return
Math.sqrt((point1.x
-
point2.x)
*
(point1.x
-
point2.x)
+
(point1.y
-
point2.y)
*
(point1.y
-
point2.y));
}
/**
*
綁定View
*/
public
static
void
attach(View
view,
MsgDrafitingListener.BubbleDisappearListener
disappearListener)
{
view.setOnTouchListener(new
MsgDrafitingListener(view.getContext(),disappearListener));
}
public
void
initPoint(float
x,
float
y)
{
mBigCirclePoint
=
new
PointF(x,y);
mLittleCirclePoint
=
new
PointF(x,y);
}
public
void
updatePoint(float
x,float
y)
{
mBigCirclePoint.x
=
x;
mBigCirclePoint.y
=
y;
invalidate();
}
public
void
setDragBitmap(Bitmap
dragBitmap)
{
this.dragBitmap
=
dragBitmap;
}
public
void
setOnToucnUpListener(OnToucnUpListener
listener)
{
mOnToucnUpListener
=
listener;
}
public
interface
OnToucnUpListener
{
//
還原
void
restore();
//
消失爆炸
void
dismiss(PointF
pointF);
}
/**
*
處理手指抬起后的操作
*/
public
void
OnTouchUp()
{
if
(mLittleCircleRadius
>
mLittleCircleRadiusMin)
{
//
回彈
ValueAnimator
值變化的動畫
0
變化到
1
ValueAnimator
animator
=
ObjectAnimator.ofFloat(1);
animator.setDuration(250);
final
PointF
start
=
new
PointF(mBigCirclePoint.x,
mBigCirclePoint.y);
final
PointF
end
=
new
PointF(mLittleCirclePoint.x,
mLittleCirclePoint.y);
animator.addUpdateListener(new
ValueAnimator.AnimatorUpdateListener()
{
@Override
public
void
onAnimationUpdate(ValueAnimator
animation)
{
float
percent
=
(float)
animation.getAnimatedValue();//
0
-
1
PointF
pointF
=
Utils.getPointByPercent(start,
end,
percent);
//更新位子
updatePoint(pointF.x,
pointF.y);
}
});
//
設(shè)置一個差值器
在結(jié)束的時候回彈
animator.setInterpolator(new
OvershootInterpolator(3f));
animator.start();
//
還要通知
TouchListener
animator.addListener(new
AnimatorListenerAdapter()
{
@Override
public
void
onAnimationEnd(Animator
animation)
{
if(mOnToucnUpListener
!=
null){
mOnToucnUpListener.restore();
}
}
});
}
else
{
//
爆炸
if(mOnToucnUpListener
!=
null){
mOnToucnUpListener.dismiss(mBigCirclePoint);
}
}
}
}
(2)自定義OnTouchListenner的代碼public
class
MsgDrafitingListener
implements
View.OnTouchListener
{
private
WindowManager
mWindowManager;
private
WindowManager.LayoutParams
params;
private
MsgDrafitingView
mMsgDrafitingView;
private
Context
context;
//
爆炸動畫
private
FrameLayout
mBombFrame;
private
ImageView
mBombImage;
private
BubbleDisappearListener
mDisappearListener;
public
MsgDrafitingListener(Context
context,BubbleDisappearListener
disappearListener)
{
mWindowManager
=
(WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
params
=
new
WindowManager.LayoutParams();
mMsgDrafitingView
=
new
MsgDrafitingView(context);
//背景透明
params.format
=
PixelFormat.TRANSPARENT;
this.context
=
context;
mBombFrame
=
new
FrameLayout(context);
mBombImage
=
new
ImageView(context);
mBombImage.setLayoutParams(new
FrameLayout.LayoutParams(Utils.dip2px(30,context),
Utils.dip2px(30,context)));
mBombFrame.addView(mBombImage);
this.mDisappearListener
=
disappearListener;
}
@Override
public
boolean
onTouch(final
View
view,
MotionEvent
motionEvent)
{
switch
(motionEvent.getAction())
{
case
MotionEvent.ACTION_DOWN:
//隱藏自己
view.setVisibility(View.INVISIBLE);
mWindowManager.addView(mMsgDrafitingView,params);
int[]
location
=
new
int[2];
view.getLocationOnScreen(location);
Bitmap
bitmap
=
getBitmapByView(view);
//y軸需要減去狀態(tài)欄的高度
mMsgDrafitingView.initPoint(location[0]
+
view.getWidth()
/
2,
location[1]+view.getHeight()/2
-Utils.getStatusBarHeight(context));
//
給消息拖拽設(shè)置一個Bitmap
mMsgDrafitingView.setDragBitmap(bitmap);
//設(shè)置OnTouchUpListener
mMsgDrafitingView.setOnToucnUpListener(new
MsgDrafitingView.OnToucnUpListener()
{
@Override
public
void
restore()
{
//還原位子
//
把消息的View移除
mWindowManager.removeView(mMsgDrafitingView);
//
把原來的View顯示
view.setVisibility(View.VISIBLE);
}
@Override
public
void
dismiss(PointF
pointF)
{
//爆炸效果
//
要去執(zhí)行爆炸動畫
(幀動畫)
//移除拖拽的view
mWindowManager.removeView(mMsgDrafitingView);
//
要在
mWindowManager
添加一個爆炸動畫
mWindowManager.addView(mBombFrame,params);
mBombImage.setBackgroundResource(R.drawable.anim_bubble_pop);
AnimationDrawable
drawable
=
(AnimationDrawable)
mBombImage.getBackground();
mBombImage.setX(pointF.x-drawable.getIntrinsicWidth()/2);
mBombImage.setY(pointF.y-drawable.getIntrinsicHeight()/2);
drawable.start();
//
等它執(zhí)行完之后我要移除掉這個
爆炸動畫也就是
mBombFrame
mBombImage.postDelayed(new
Runnable()
{
@Override
public
void
run()
{
mWindowManager.removeView(mBombFrame);
//
通知一下外面該消失
if(mDisappearListener
!=
null){
mDisappearListener.dismiss(view);
}
}
},getAnimationDrawableTime(drawable));
}
});
break;
case
MotionEvent.ACTION_MOVE:
mMsgDrafitingView.updatePoint(motionEvent.getRawX(),
motionEvent.getRawY()
-
Utils.getStatusBarHeight(context));
break;
case
MotionE
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 搭式生物濾池課程設(shè)計
- 造價咨詢有限公司管理制度
- 搬運(yùn)小車課程設(shè)計
- 趣味識字吃飯課程設(shè)計
- 攪拌機(jī)機(jī)械原理課程設(shè)計
- 插花與花卉栽培課程設(shè)計
- 路由交換課程設(shè)計小型
- 控制與檢測綜合課程設(shè)計
- 課程設(shè)計風(fēng)荷載計算
- 臨時發(fā)電機(jī)房搭建合同
- 留守兒童的家庭教育-課件
- 第16課 兩次鴉片戰(zhàn)爭課件四
- 多囊卵巢綜合癥新版培訓(xùn)課件
- 鐵路安全教育課教案(通用10篇)
- 提高預(yù)制樓梯安裝一次性合格率-QC成果報告
- 電線裝配制程cableassemblyprocessinstruction
- 幼兒園中班語言《誰偷吃了》課件1
- 普通高中數(shù)學(xué)課程標(biāo)準(zhǔn)
- 生態(tài)環(huán)境觀測站建設(shè)項目可行性研究報告
- 《新生兒黃疸》課件
- 科創(chuàng)板問題測試題庫300題試題及答案
評論
0/150
提交評論