首先感谢这两篇文章提供的思路和代码
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