【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失_第1頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失_第2頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失_第3頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失_第4頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中通過貝塞爾曲線實(shí)現(xiàn)消息拖拽消失_第5頁
已閱讀5頁,還剩13頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論