android自定义view之模拟qq消息拖拽删除效果

android自定义view之模拟qq消息拖拽删除效果,第1张

概述这个模拟功能的实现主要依靠了PATH和二阶贝塞尔曲线。首先上一张图来简单看一下:

这个模拟功能的实现主要依靠了PATH和二阶贝塞尔曲线。首先上一张图来简单看一下:

这个模拟功能有以下几个特点:

在开始的时候点击圆以外的区域不会触发拖动事件 点击圆的时候可以拖拽,此时会有一个拉伸效果,连接大圆和小圆 拉伸到一定距离(自己设定)以后两个圆会断开,此时即使再拖拽进距离之内的时候也不会再产生已经断开的连接 在距离之内松手的时候会回d会原位置,并伴有一个d跳动画

介绍了这么多,看过我前边文章的朋友应该会有一个基本思路。

暴露接口

这个模拟功能共分为三部分,一个是那个小圆,固定的位置,一个是那个大圆,可以移动,还有一部分就是中间的连接部分,会跟随大圆一起延伸。

首先看一下都有哪些接口可以调用:

 public voID setMinR(float minR) {    this.minR = minR;  }  public voID setMaxR(float maxR) {    this.maxR = maxR;  }  public voID setbrokedistance(float distance) {    this.brokedistance = distance;  }

第一个setMinR是设置小圆的半径,第二个setMaxR是设置大圆的半径,第三个setbrokedistance是设置断开的距离,也就是小圆和大圆的圆心之间的最大连接距离。

初始化

public Buble(Context context) {    super(context);    init();  }  public Buble(Context context,@Nullable AttributeSet attrs) {    super(context,attrs);    init();  }

简单的看一下初始化方法。

private voID init() {    paint = new Paint();    paint.setStyle(Paint.Style.FILL);    paint.setcolor(color.GREEN);    paint.setAntiAlias(true);  }

其实只有一个画笔,这里可以为各个区域分别设置画笔。

绘制图形

protected voID onDraw(Canvas canvas) {    super.onDraw(canvas);    drawOriginalCircle(canvas);    if (!canbroke) {      drawMoveCircle(canvas);      drawBCurve(canvas);    }  }

这三个方法相对简单,drawOriginalCircle是画中心的小圆,然后canbroke是一个开关,控制是否执行画移动的圆和画弧线。

 private voID drawOriginalCircle(Canvas canvas) {    canvas.drawCircle(getWIDth() / 2,getHeight() / 2,minR,paint);  }  private voID drawMoveCircle(Canvas canvas) {    canvas.drawCircle(moveX,moveY,maxR,paint);  }  private voID drawBCurve(Canvas canvas) {    canvas.drawPath(path,paint);  }

注意,moveX,moveY和path都是变化的,所以在他们的值发生改变以后千万不要忘记invalIDate。

path的连接

关于path的文章网上一大堆。

此处的难点主要是大圆和小圆之间的连接。用一张图简单表示一下:

基本就是这个样子,path的路径就是那个黑色的类似于漏斗一样的东西。此处要计算的角度需要用到三角函数关系式,简单表示一下:

图中标出的两个角度是相等的

double angle = Math.atan((offsetX - minCircleX) / (offsetY - minCircleY));

求出这个角度(offsetX是移动圆心的坐标)。

这样就可以算出四个点的坐标了。

private voID setPath(float offsetX,float offsetY) {    float minCircleX = (float) getWIDth() / 2;    float minCircleY = (float) getHeight() / 2;    double angle = Math.atan((offsetX - minCircleX) / (offsetY - minCircleY));    float x1 = (float) (minCircleX + Math.cos(angle) * minR);    float y1 = (float) (minCircleY - Math.sin(angle) * minR);    float x2 = (float) (offsetX + Math.cos(angle) * maxR);    float y2 = (float) (offsetY - Math.sin(angle) * maxR);    float x3 = (float) (offsetX - Math.cos(angle) * maxR);    float y3 = (float) (offsetY + Math.sin(angle) * maxR);    float x4 = (float) (minCircleX - Math.cos(angle) * minR);    float y4 = (float) (minCircleY + Math.sin(angle) * minR);    float centerX = minCircleX + (offsetX - minCircleX) / 2;    float centerY = minCircleY + (offsetY - minCircleY) / 2;    path.reset();    path.moveto(minCircleX,minCircleY);    path.lineto(x1,y1);    path.quadTo(centerX,centerY,x2,y2);    path.lineto(x3,y3);    path.quadTo(centerX,x4,y4);    path.lineto(minCircleX,minCircleY);    path.close();  }

注意quadTo的四个参数的意义,前两个是你的锚点的坐标,后两个是你要移动到那个点的位置的坐标。

触摸事件

这个直接上代码来实现思路吧,没什么好讲的。

 switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        this.canbroke = false;        moveX = event.getX();        moveY = event.getY();        touchArea = !setCanbroke(moveX,maxR);        break;      case MotionEvent.ACTION_MOVE:        if (touchArea) {          moveX = event.getX();          moveY = event.getY();          if (setCanbroke(moveX,brokedistance)) {            touchArea = false;            this.canbroke = true;          } else {            setPath(moveX,moveY);          }          invalIDate();        }        break;      case MotionEvent.ACTION_UP:        Log.d("aaa","actionUp" + touchArea);        if (touchArea) {          resetCircle(event.getX(),event.getY());        }        break;    }    return true;

这里主要说明一下这个setCanbroke:

 private boolean setCanbroke(float offsetX,float offsetY,float brokedistance) {    float minCircleX = (float) getWIDth() / 2;    float minCircleY = (float) getHeight() / 2;    return (offsetX - minCircleX) * (offsetX - minCircleX) +        (offsetY - minCircleY) * (offsetY - minCircleY) > brokedistance * brokedistance;  }

这个表示是否超出了最大移动距离,超出则返回真,未超出则返回假。同时在touchArea的设定中它也用用到了,主要是判断点击区域是否在圆圈上。

最后还要讲一下这个resetCicle,这个是一个属性动画来控制返回原点的d性动画:

private voID resetCircle(float x,float y) {    valueAnimatorX = ValueAnimator.offloat(x,(float) getWIDth() / 2);    valueAnimatorY = ValueAnimator.offloat(y,(float) getHeight() / 2);    valueAnimatorX.removeAllUpdateListeners();    valueAnimatorY.removeAllUpdateListeners();    valueAnimatorX.setInterpolator(new BounceInterpolator());    valueAnimatorY.setInterpolator(new BounceInterpolator());    valueAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @OverrIDe      public voID onAnimationUpdate(ValueAnimator animation) {        tempX = (float) animation.getAnimatedValue();        moveX = tempX;      }    });    valueAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @OverrIDe      public voID onAnimationUpdate(ValueAnimator animation) {        tempY = (float) animation.getAnimatedValue();        moveY = tempY;        setPath(tempX,tempY);        postInvalIDate();      }    });    set.playTogether(valueAnimatorX,valueAnimatorY);    set.start();  }

其中的插值器是BounceInterpolator,类似于小球d跳的动画,在我前边的文章中有介绍。

最后来看一下不会断开的效果,相当有意思:

关于自定义view的文章会暂时到这里,下一步准备更新自定义viewgroup的文章。相对于自定义view会稍微简单一点。

demo下载地址:PathApplication_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的android自定义view之模拟qq消息拖拽删除效果全部内容,希望文章能够帮你解决android自定义view之模拟qq消息拖拽删除效果所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1146552.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存