之前写过一篇文章:自定义view——贝塞尔曲线之仿qq消息气泡拖拽
http://blog.csdn.net/qq_24675479/article/details/79430672
这里将完善上个项目,让其所有view都可以首先拖动爆炸
首先工具类
public class BubbleUtils {
/**
* dip 转换成 px
*
* @param dip
* @param context
* @return
*/
public static int dip2px(float dip, Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
/**
* 获取状态栏的高度
*/
public static int getStatusBarHeight(Context context) {
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return context.getResources().getDimensionPixelOffset(resourceId);
}
return dip2px(25, context);
}
/**
* 根据百分比获取两点之间的某个点坐标
*/
public static PointF getPointByPercent(PointF p1,PointF p2,float percent){
return new PointF(evaluateValue(percent,p1.x,p2.x)
,evaluateValue(percent,p1.y,p2.y));
}
/**
* 根据分度值,计算从start到end中,fraction位置的值。fraction范围为0 -> 1
*
* @param fraction
* = 1
* @param start
* = 10
* @param end
* = 3
* @return
*/
public static float evaluateValue(float fraction, Number start, Number end) {
// start = 10 end = 2
//fraction = 0.5
// result = 10 + (-8) * fraction = 6
return start.floatValue()+(end.floatValue()-start.floatValue())*fraction;
}
}
修改MessageBubbleView
public class MessageBubbleView extends View {
// 两个圆的圆形
private PointF mFixationPoint, mDragPoint;
// 拖拽圆的半径
private int mDragRadius = 10;
private Paint mPaint;
// 固定圆的最大半径(初始半径)
private int mFixationRadiusMax = 7;
private int mFixationRadiusMin = 3;
private int mFixationRadius;
private Bitmap mDragBitmap;
public MessageBubbleView(Context context) {
this(context, null);
}
public MessageBubbleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MessageBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mDragRadius = dp2x(mDragRadius);
mFixationRadiusMax = dp2x(mFixationRadiusMax);
mFixationRadiusMin = dp2x(mFixationRadiusMin);
}
private int dp2x(int dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
}
@Override
protected void onDraw(Canvas canvas) {
if (mDragPoint == null || mFixationPoint == null) {
return;
}
//绘制拖拽的圆
canvas.drawCircle(mDragPoint.x, mDragPoint.y, mDragRadius, mPaint);
//获得两个圆之间的距离
double distance = getDistance(mDragPoint, mFixationPoint);
mFixationRadius = (int) (mFixationRadiusMax - distance / 14);
Path bezeierPath = getBezeierPath();
if (bezeierPath != null) {//当大于最小的时候才绘制
//绘制固定的圆
canvas.drawCircle(mFixationPoint.x, mFixationPoint.y, mFixationRadius, mPaint);
//绘制贝塞尔曲线
canvas.drawPath(bezeierPath, mPaint);
}
if (mDragBitmap != null) {
//绘制一个bitmap
canvas.drawBitmap(mDragBitmap, mDragPoint.x - mDragBitmap.getWidth() / 2
, mDragPoint.y - mDragBitmap.getHeight() / 2, null);
}
}
/**
* 获取两个圆之间的距离
*/
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));
}
/* @Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float downX = event.getX();
float downY = event.getY();
initPoint(downX, downY);
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
updatePoint(moveX, moveY);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}*/
/**
* 更新拖拽点的坐标
*/
public void updatePoint(float moveX, float moveY) {
mDragPoint.x = moveX;
mDragPoint.y = moveY;
invalidate();
}
/**
* 初始化点
*/
public void initPoint(float downX, float downY) {
mFixationPoint = new PointF(downX, downY);
mDragPoint = new PointF(downX, downY);
invalidate();
}
/**
* 获得贝塞尔的曲线
*
* @return
*/
public Path getBezeierPath() {
if (mFixationRadius < mFixationRadiusMin) {
// 超过一定距离 贝塞尔和固定圆都不要画了
return null;
}
Path bezeierPath = new Path();
//求角a
//求斜率
float dy = mDragPoint.y - mFixationPoint.y;
float dx = mDragPoint.x - mFixationPoint.x;
float tanA = dy / dx;
//求出角a
double arcTanA = Math.atan(tanA);
//p0
float p0x = (float) (mFixationRadius * Math.sin(arcTanA) + mFixationPoint.x);
float p0y = (float) (mFixationPoint.y - mFixationRadius * Math.cos(arcTanA));
//p1
float p1x = (float) (mDragRadius * Math.sin(arcTanA) + mDragPoint.x);
float p1y = (float) (mDragPoint.y - mDragRadius * Math.cos(arcTanA));
//p2
float p2x = (float) (mDragPoint.x - mDragRadius * Math.sin(arcTanA));
float p2y = (float) (mDragPoint.y + mDragRadius * Math.cos(arcTanA));
//p3
float p3x = (float) (mFixationPoint.x - mFixationRadius * Math.sin(arcTanA));
float p3y = (float) (mFixationPoint.y + mFixationRadius * Math.cos(arcTanA));
//绘制贝塞尔曲线
bezeierPath.moveTo(p0x, p0y);
//绘制第一条线
PointF controlPoint = getControlPoint();
bezeierPath.quadTo(controlPoint.x, controlPoint.y, p1x, p1y);//控制点+目标坐标
//绘制到第二条
bezeierPath.lineTo(p2x, p2y);
bezeierPath.quadTo(controlPoint.x, controlPoint.y, p3x, p3y);
//最后闭合
bezeierPath.close();
return bezeierPath;
}
/**
* 获取控制点
*/
public PointF getControlPoint() {
return new PointF((mDragPoint.x + mFixationPoint.x) / 2, (mDragPoint.y + mFixationPoint.y) / 2);
}
/**
* 设置可拖拽的事件
*/
public static void attach(View view, BubbleMessageTouchListener.BubbleDisappearListener disappearListener) {
view.setOnTouchListener(new BubbleMessageTouchListener(view, view.getContext(),disappearListener));
}
public void setDragBitmap(Bitmap dragBitmap) {
this.mDragBitmap = dragBitmap;
}
//手指放开
public void handleActionUp() {
if (mFixationRadius > mFixationRadiusMin) {
//回弹 ValueAnimator 值变化的动画 0 变化到 1
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(250);
final PointF start=new PointF(mDragPoint.x,mDragPoint.y);
final PointF end=new PointF(mFixationPoint.x,mFixationPoint.y);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float precent = (float) animation.getAnimatedValue();//获得当前百分比
PointF pointF=BubbleUtils.getPointByPercent(start,end,precent);
//PointF pointF=BubbleUtils.getPointByPercent(mDragPoint,mFixationPoint,precent);//会没有回弹效果
updatePoint(pointF.x,pointF.y);
}
});
//差值器 回弹的时候弹一下
animator.setInterpolator(new OvershootInterpolator(3f));
animator.start();
// 还要通知 TouchListener 移除当前View 然后显示静态的 View
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//动画结束,移除
if(mListener!=null){
mListener.restore();
}
}
});
} else {
// 爆炸
if(mListener != null){
mListener.dismiss(mDragPoint);
}
}
}
private BubbleDisappearListener mListener;
public void setListener(BubbleDisappearListener mListener) {
this.mListener = mListener;
}
public interface BubbleDisappearListener {
//恢复
void restore();
//爆炸消失
void dismiss(PointF mDragPoint);
}
}
BubbleMessageTouchListener:触摸事件终究处理类
public class BubbleMessageTouchListener implements View.OnTouchListener, MessageBubbleView.BubbleDisappearListener {
// 原来需要拖动爆炸的View
private View mStaticView;
//消失后创建一个view
private MessageBubbleView mMessageBubbleView;
//创建一个windowManager
private WindowManager mWindowManager;
private WindowManager.LayoutParams mParams;
private Context mContext;
//爆炸
private FrameLayout mBmobFrameLayout;
private ImageView mBombImageView;
BubbleDisappearListener mDisappearListener;
public BubbleMessageTouchListener(View view, Context context, BubbleDisappearListener disappearListener) {
this.mStaticView = view;
mMessageBubbleView = new MessageBubbleView(context);
mMessageBubbleView.setListener(this);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mParams = new WindowManager.LayoutParams();
//设置背景透明
mParams.format = PixelFormat.TRANSPARENT;
this.mContext = context;
this.mDisappearListener = disappearListener;
//初始化爆炸
mBmobFrameLayout = new FrameLayout(mContext);
mBombImageView = new ImageView(mContext);
mBombImageView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
, ViewGroup.LayoutParams.WRAP_CONTENT));
mBmobFrameLayout.addView(mBombImageView);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mWindowManager.addView(mMessageBubbleView, mParams);
//应该是中心位置而不是按下的位置
// mMessageBubbleView.initPoint(event.getRawX(), event.getRawY() - BubbleUtils.getStatusBarHeight(mContext));
int[] location = new int[2];
mStaticView.getLocationOnScreen(location);
mMessageBubbleView.initPoint(location[0] + mStaticView.getWidth(),
location[1] + mStaticView.getWidth() / 2 - BubbleUtils.getStatusBarHeight(mContext));
Bitmap bitmap = getBitmapByView(mStaticView);
//给mMessageBubbleView添加一个拖拽的view
mMessageBubbleView.setDragBitmap(bitmap);
//控件消失
mStaticView.setVisibility(View.INVISIBLE);
break;
case MotionEvent.ACTION_MOVE:
mMessageBubbleView.updatePoint(event.getRawX(), event.getRawY() - BubbleUtils.getStatusBarHeight(mContext));
break;
case MotionEvent.ACTION_UP:
mMessageBubbleView.handleActionUp();
break;
}
return true;
}
/**
* 从view中获取bitmap
*/
private Bitmap getBitmapByView(View view) {
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
//恢复
@Override
public void restore() {
mWindowManager.removeView(mMessageBubbleView);
mStaticView.setVisibility(View.VISIBLE);
}
@Override
public void dismiss(PointF pointF) {
// 要去执行爆炸动画 (帧动画)
// 原来的View的View肯定要移除
mWindowManager.removeView(mMessageBubbleView);
mWindowManager.addView(mBmobFrameLayout, mParams);
//添加一个动画效果
mBombImageView.setBackgroundResource(R.drawable.anim_bubble_pop);
AnimationDrawable drawable = (AnimationDrawable) mBombImageView.getBackground();
//设置爆炸图片的位置
mBombImageView.setX(pointF.x - drawable.getIntrinsicWidth() / 2);
mBombImageView.setY(pointF.y - drawable.getIntrinsicHeight() / 2);
drawable.start();
// 等它执行完之后要移除掉这个 爆炸动画也就是 mBombFrame
mBombImageView.postDelayed(new Runnable() {
@Override
public void run() {
mWindowManager.removeView(mBmobFrameLayout);
//通知外面要消失
mDisappearListener.dismiss(mStaticView);
}
}, getAnimationDrawableTime(drawable));
}
private long getAnimationDrawableTime(AnimationDrawable drawable) {
long time = 0;
int frames = drawable.getNumberOfFrames();
for (int i = 0; i < frames; i++) {
time += drawable.getDuration(i);
}
return time;
}
/**
* 提供接口给外部
*/
public interface BubbleDisappearListener {
void dismiss(View view);
}
}