【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失_第1页
【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失_第2页
【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失_第3页
【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失_第4页
【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】怎么在Android中通过贝塞尔曲线实现消息拖拽消失

本篇文章为大家展示了怎么在Android中通过贝塞尔曲线实现消息拖拽消失,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。分析(用到的知识点):

(1)ValueAnimator(数值生成器)用于生成数值,可以设置差值器来改变数字的变化幅度。(2)ObjectAnimator(动画生成器)用于生成各种属性,布局动画,同样也可以设置差值器来改变效果。(3)贝塞尔一阶曲线(4)自定义View的基础知识(5)WindowManager使view拖拽能显示在整个屏幕的任何地方,而不是局限于父布局内具体实现方法一、首先我们要实现基础效果基础效果是点击屏幕任意一点能出现消息拖拽的效果,但是此时我们不用管我们拖动的View,只需要完成大致模型。该部分的难点在于贝塞尔一阶曲线的怎么实现。基础效果图

分析:(1)点击任意一点画出两个圆,和一个有贝塞尔曲线组成的path路径(2)随着拖动距离的增加原点的圆半径逐渐缩小,当距离达到一定大以后原点的圆和贝塞尔曲线组成的path不再显示贝塞尔曲线的画法首先我们需要求出角a的大小,根据角a来求到A,B,C,D的坐标位子,然后求到控制点E点的坐标,通过Path.quadTo()方法来连接A,B和C,D两条贝塞尔曲线。各点坐标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);

//

移动

//

两个点

PointF

controlPoint

=

getControlPoint();

//

画了第一条

第一个点(控制点,两个圆心的中心点),终点

bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);

//

画第二条

bezeierPath.lineTo(Cx,Cy);

//

链接到

bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);

bezeierPath.close();

return

bezeierPath;

}

二、完善代码

这部分我们需要完善所有代码,实现代码的分离,使得所用View都能被拖动,且需要创建一个监听器来监听View是否拖动结束了,结束后调用回调方法以便需要做其他处理。需要完成的功能:(1)将传入的View画出来(2)在手指抬起时判断是爆炸还是回弹(3)完成回弹和爆炸的代码部分(4)回弹或者爆炸结束后调用回调通知动画结束(5)使用WindowManager把自定义拖拽View加进去,隐藏原来得View实现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);

//

移动

//

两个点

PointF

controlPoint

=

getControlPoint();

//

画了第一条

第一个点(控制点,两个圆心的中心点),终点

bezeierPath.quadTo(controlPoint.x,controlPoint.y,Bx,By);

//

画第二条

bezeierPath.lineTo(Cx,Cy);

//

链接到

bezeierPath.quadTo(controlPoint.x,controlPoint.y,Dx,Dy);

bezeierPath.close();

return

bezeierPath;

}

/**

*

获得控制点距离

*/

public

PointF

getControlPoint()

{

return

new

PointF((mLittleCirclePoint.x+mBigCirclePoint.x)/2,(mLittleCirclePoint.y+mBigCirclePoint.y)/2);

}

/**

*

获得两点之间的距离

*/

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);

}

});

//

设置一个差值器

在结束的时候回弹

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轴需要减去状态栏的高度

mMsgDrafitingView.initPoint(location[0]

+

view.getWidth()

/

2,

location[1]+view.getHeight()/2

-Utils.getStatusBarHeight(context));

//

给消息拖拽设置一个Bitmap

mMsgDrafitingView.setDragBitmap(bitmap);

//设置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)

{

//爆炸效果

//

要去执行爆炸动画

(帧动画)

//移除拖拽的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();

//

等它执行完之后我要移除掉这个

爆炸动画也就是

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. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论