Android 仿QQ 聊天消息拖拽效果

可拖拽的气泡效果

自定义view WateView

    public class WateView extends FrameLayout {

    //定义一个文本控件
    private TextView textView;
    //定义控件的坐标   文本框的 初始坐标
    private PointF initPosition;
     //手指是否触摸到了控件
    private boolean isClcked = false ;
    //手指移动到的坐标  终点坐标
    private  PointF movePosition;
    //绘制的圆的半径
    private float mRadius = 40;
    //绘制的画笔
     private Paint paint ;
    //存储连接桥路径对象
    private Path path;
    // 判断文本框是否离开了某个范围
    private  boolean  isOut =false ;
    public WateView(Context context) {
        super(context);
        init();
    }

    /**
    * 初始化整个效果的控件
    */

    private void  init(){
        initPosition = new PointF(500,500);
        movePosition = new PointF();
         path = new Path();
        //初始化画笔
            paint = new Paint();
        paint.setColor(Color.RED);
        //设置画笔的样式 填充
    paint.setStyle(Paint.Style.FILL);
    textView = new TextView(getContext());
    textView.setPadding(20,20,20,20);
    textView.setText("99+");
    textView.setTextColor(Color.WHITE);

    textView.setBackgroundResource(R.drawable.textview_bg);
    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);

    textView.setLayoutParams(layoutParams);
    this.addView(textView);
    }






    /**
     * 回执包括本身
    * @param canvas
    */  
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    /**
    * 绘制当前控件里面的内容 里面的控件
    * @param canvas
     */
    @Override
     protected void dispatchDraw(Canvas canvas) {
    // 保存canvasde 状态
    canvas.save();
    if (isClcked){
    textView.setX(movePosition.x-textView.getWidth()/2);
    textView.setY(movePosition.y-textView.getHeight()/2);
        drawPath();
        if (!isOut){  // 在范围内允许画    
            //画第一个圆
            canvas.drawCircle(initPosition.x,initPosition.y,mRadius,paint);
            // 画第二个圆  终点的圆
            canvas.drawCircle(movePosition.x,movePosition.y,mRadius,paint);
            // 画连接桥
            canvas.drawPath(path,paint);
        }
    }else {
        textView.setX(initPosition.x-textView.getWidth()/2);
        textView.setY(initPosition.y-textView.getHeight()/2);
    }
    // 回复canvas状态
    canvas.restore();
    super.dispatchDraw(canvas);
}



/**
 * 找到绘制连接桥的四个点 并且用贝塞尔曲线链接起来
 */


public void drawPath(){
    // 获取到终点与起点x  y 坐标的差值
   float widthX =  movePosition.x -initPosition.x;
    float widthY =  movePosition.y -initPosition.y; 
    //获取两个点之间的直线距离  勾股定理
    float sss= (float) Math.sqrt(Math.pow(widthX,2)+Math.pow(widthY,2));
    mRadius = 40-sss/20;
    if (sss>=300){
       isOut = true ;
    }else {
       isOut =false ;
    }

    // 得到三角形的正切值
    double atan = Math.atan(widthY/widthX);
    // 获取到offsetX 长度   offsetY
    float offsetX = (float) (mRadius*Math.sin(atan));
    float offsetY = (float) (mRadius*Math.cos(atan));、
    //获取到a坐标
    float ax =  initPosition.x+offsetX;
    float ay =  initPosition.y-offsetY;
    //获取到b坐标
    float bx =  movePosition.x+offsetX;
    float by =  movePosition.y-offsetY;
   //获取到c坐标
    float cx =  movePosition.x-offsetX;
    float cy =  movePosition.y+offsetY;
   //获取到d坐标
    float dx =  initPosition.x-offsetX;

    float dy =  initPosition.y+offsetY;
    // 获取到起点坐标和终点坐标的中心点
    float conX = (initPosition.x+movePosition.x)/2;
    float conY = (initPosition.y+movePosition.y)/2;
    // 初始化path对象
    path.reset();
    //将起点移动到A坐标
    path.moveTo(ax,ay);

    //从A点链接到B点   // quadTo();第一个参数 是弯曲的点的x点  第二个参数是弯曲点的y点 

    path.quadTo(conX,conY,bx,by);
    //从B点链接到C点
    path.lineTo(cx,cy);
    //从c点链接到d点 d
    path.quadTo(conX,conY,dx,dy);
    //从d点链接到A点

    path.lineTo(ax,ay);
}



@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
        movePosition.set(initPosition.x,initPosition.y);
            //判断当前点击的位置是否在文本控件里面
            Rect rect = new Rect();// 封装文本控件范围的对象

            int [] location = new int [2];
            //获取textview控件窗体中X Y 坐标

            textView.getLocationOnScreen(location);
            //初始化rect对象
            rect.left = location[0];
            rect.top = location[1];
            rect.right = location[0]+textView.getWidth();
            rect.bottom = location[1]+textView.getHeight();
            // 判断当前点击的坐标是否在范围内
             if (rect.contains((int)event.getRawX(),(int)event.getRawY())){
                    isClcked = true ;   
            }
                break;

        case MotionEvent.ACTION_UP:
            isClcked = false ;
            break;

        case MotionEvent.ACTION_MOVE:
            movePosition.set((int)event.getX(),(int)event.getY());

            break;

    }

    // 通过这个API调用dispatchDraw方法
    postInvalidate();

     return true;
}
    ------注释----
    getX()  getY(); 取值是相对于父控件而言 
    getRawX() getRawY()  取值相对于屏幕而言

textview_bg

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ff0404"/>
    <stroke android:width="0dp" android:color="#ff0404"/>
    <corners android:radius="50dp"/>
    </shape>

界面布局文件

具体使用  

 

 得到帮助的朋友点点赞   也可以对小弟打赏喔

发布了8 篇原创文章 · 获赞 3 · 访问量 974

猜你喜欢

转载自blog.csdn.net/flying0916/article/details/97761971