模仿QQ拖动清除消息提示

首先感谢这两篇文章提供的思路和代码

http://blog.csdn.net/chenupt/article/details/41478303

http://blog.csdn.net/singwhatiwanna/article/details/42614953

首先看下效果图:


实现原理:

点击屏幕,遍历所有的view,匹配点击的屏幕坐标,是否在某个红点提示的view范围内,是的话,计算红点提示的view,在自定义的布局上对应的坐标位置,计算出来的这个坐标,当做其中的一个圆的圆心,在拖动的过程中,得到的另外的坐标,当做是另外一个圆的圆心,在拖动的过程中,半径变化,但两个圆始终保持半径一样,利用贝塞尔曲线,画出拖动的效果。

重要的代码,都有注释,代码也比较少,代码如下:

/**   
 * @Title: DragTipsLayout.java 
 * @Package com.eeb.dragredview 
 * @Description: TODO(模仿QQ拖动消息提示) 
 * @author luquan yebo0505@foxmail.com   
 * @date 2015-1-4 上午11:57:48 
 * @version V1.0   
 */
package com.eeb.dragtipslayout;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public class DragTipsLayout extends LinearLayout {
	// 默认定点圆半径
	public static final float DEFAULT_RADIUS = 10;
	private int[] mLocationInScreen = new int[2];

	private Paint paint;
	private Path path;
	private boolean dragging;
	// 手势滑动是的动态坐标
	float touchX = 0;
	float touchY = 0;

	// 锚点坐标,两个圆圆心(手势滑动坐标和要拖动的bageview的中心)的中间点
	float anchorX = 0;
	float anchorY = 0;

	// 定点圆半径
	float radius = DEFAULT_RADIUS;

	// 要拖动的View的中心坐标
	private float mCenterX;
	private float mCenterY;
	private View targetView;

	public DragTipsLayout(Context context) {
		super(context);
		init();
	}

	public DragTipsLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		this.getLocationOnScreen(mLocationInScreen);//DragTisLayout 在屏幕上的位置
	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.dispatchDraw(canvas);
		if (dragging) {
			// 画拖动的效果,这里计算的是在拖动的过程中,半径变化,但两个圆始终保持半径一样,如果半径一大一小不同,需要另外计算
			float distance = (float) Math.sqrt(Math.pow(touchY - mCenterY, 2)
					+ Math.pow(touchX - mCenterX, 2));
			radius = -distance / 20 + DEFAULT_RADIUS;
			paint.setColor(Color.RED);
			if (radius > 5) {
				float offsetX = (float) (radius * Math.sin(Math
						.atan((touchY - mCenterY) / (touchX - mCenterX))));
				float offsetY = (float) (radius * Math.cos(Math
						.atan((touchY - mCenterY) / (touchX - mCenterX))));

				float x1 = mCenterX - offsetX;
				float y1 = mCenterY + offsetY;

				float x2 = touchX - offsetX;
				float y2 = touchY + offsetY;

				float x3 = touchX + offsetX;
				float y3 = touchY - offsetY;

				float x4 = mCenterX + offsetX;
				float y4 = mCenterY - offsetY;

				path.reset();
				path.moveTo(x1, y1);
				path.quadTo(anchorX, anchorY, x2, y2);
				path.lineTo(x3, y3);
				path.quadTo(anchorX, anchorY, x4, y4);
				path.lineTo(x1, y1);

				canvas.drawCircle(mCenterX, mCenterY, radius, paint);
				canvas.drawPath(path, paint);
			}
			
			if (targetView != null) {
				//计算字体所占的位置大小,获取拖动的badge大小,以确定在拖动过程中,字体居中,画出拖动的view跟badgeview一样
				String num = ((BadgeView) targetView).getText().toString();
				Rect rect = new Rect();
				paint.getTextBounds(num, 0, num.length(), rect);
				RectF rectF = new RectF(touchX-targetView.getWidth()/2,touchY-targetView.getHeight()/2,touchX+targetView.getWidth()/2,touchY+targetView.getHeight()/2);
				canvas.drawOval(rectF, paint);
				paint.setColor(Color.WHITE);
				canvas.drawText(num,touchX-rect.centerX(), touchY-rect.centerY(), paint);
			}
		}
	}

	private void init() {
		setWillNotDraw(false);
		path = new Path();
		paint = new Paint();
		paint.setAntiAlias(true);
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		paint.setStrokeWidth(1);
		paint.setColor(Color.RED);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			findTouchTarget(this, event.getRawX(), event.getRawY());
			touchX = event.getX();
			touchY = event.getY();
			anchorX = touchX;
			anchorY = touchY;
			break;
		case MotionEvent.ACTION_MOVE:
			touchX = event.getX();
			touchY = event.getY();
			anchorX = (mCenterX + touchX) / 2;
			anchorY = (mCenterY + touchY) / 2;
			break;
		case MotionEvent.ACTION_UP:
			if (radius > 5 && targetView != null)
				targetView.setVisibility(View.VISIBLE);
			dragging = false;
			break;
		}
		invalidate();
		//如果没有拖动, 事件分发不做处理,正常使用点击,长按事件,如果return true,可以理解为事件不传递到子类,各个子view的点击,滑动无效
		if(dragging)
			return true;
		else
			return super.dispatchTouchEvent(event);
			
	}

	//遍历DragTipsLayout下所有的view
	private void findTouchTarget(ViewGroup viewGrop, float x, float y) {
		for (int i = 0; i < viewGrop.getChildCount(); i++) {
			View temp = viewGrop.getChildAt(i);
			if (temp instanceof ViewGroup) {
				ViewGroup tempViewGrop = (ViewGroup) temp;
				findTouchTarget(tempViewGrop, x, y);//递归查找
			} else if (temp instanceof BadgeView
					&&isTouchBadgeView(temp, x, y)){//判断是否是BadgeView并且点击屏幕的坐标在BadgeView的区域范围内
					dragging = true;
			}
		}
	}

	private boolean isTouchBadgeView(View view, float x, float y) {
		
		int[] location = new int[2];
		view.getLocationOnScreen(location);
		int left = location[0];
		int top = location[1];
		int right = left + view.getMeasuredWidth();
		int bottom = top + view.getMeasuredHeight();
		if (view.getVisibility() == View.VISIBLE && y >= top && y <= bottom
				&& x >= left && x <= right) {
			//依据遍历找到的BadgeView,计算对应在DragTipsLayout上的坐标点
			mCenterX = (left + right) / 2 - - mLocationInScreen[0];
			mCenterY = (top+bottom)/2 - mLocationInScreen[1];
			targetView = view;
			
			view.setVisibility(View.INVISIBLE);
			return true;
		}
		return false;
	}

}

完整代码下载

欢迎转载,但请说明出处http://blog.csdn.net/yebo0505/article/details/42779441


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值